Skip to main content

Contract Specs

Overview

OnchainPoints Contract is the heart of the ecosystem. It manages points, activities, and rewards. It also defines interfaces for other apps in the ecosystem to use Points issued through various activities.

Contract Details

  • License: MIT
  • Solidity Version: ^0.8.24
  • Inheritance: Initializable, OwnableUpgradeable, UUPSUpgradeable, EIP712Upgradeable, ReentrancyGuardUpgradeable
  • Source Code: OnchainPoints

Data Structures

Structs

Request

struct Request {
uint256 deadline;
string nonce;
uint256 amount;
}

Represents a spending request:

  • deadline: Timestamp after which the request is invalid.
  • nonce: Unique identifier to prevent replay attacks.
  • amount: Amount of tokens to spend.

Activity

struct Activity {
string name;
uint256 lockupEndTimestamp;
uint256 createdAt;
}

Stores information about an activity:

  • name: Name of the activity.
  • lockupEndTimestamp: When the lockup period for rewards ends.
  • createdAt: Timestamp of activity creation.

DelegatedRequest

struct DelegatedRequest {
uint256 deadline;
string nonce;
uint256 amount;
address owner;
}

Similar to Request, but for delegated spending:

  • owner: Address of the token owner for delegated spending.

Mappings

mapping(string => Activity) public activities;
mapping(string => mapping(address => bool)) public activityRewardsClaimed;
mapping(string => mapping(address => uint256)) public activityClaimAmount;
mapping(string => mapping(address => uint256)) public activityRemainingAmount;
mapping(string => mapping(address => bool)) public activityRewardsWithdrawn;
mapping(address => string[]) public userActivities;
mapping(address => uint256) public userBalance;
mapping(address => uint256) public depositedBalance;
mapping(address => uint256) public referenceUserBalance;
mapping(uint256 => mapping(address => uint256)) public dailySpendings;
mapping(address => bool) public authorizedAddresses;
mapping(address => bool) public adminAddresses;
mapping(address => mapping(bytes32 => bool)) public nonces;
mapping(address => mapping(address => uint256)) public allowances;
  • activities: Stores all created activities.
  • activityRewardsClaimed: Tracks claimed rewards per activity and user.
  • activityClaimAmount: Amount of rewards claimed per activity and user.
  • activityRemainingAmount: Remaining locked rewards per activity and user.
  • activityRewardsWithdrawn: Tracks withdrawn rewards per activity and user.
  • userActivities: List of activities each user participated in.
  • userBalance: Current balance of each user.
  • depositedBalance: Amount of tokens deposited by each user.
  • referenceUserBalance: Reference balance for calculating spending limits.
  • dailySpendings: Tracks daily spending per user.
  • authorizedAddresses: Addresses authorized for certain operations.
  • adminAddresses: Addresses with admin privileges.
  • nonces: Tracks used nonces to prevent replay attacks.
  • allowances: Stores allowances for delegated spending.

State Variables

uint256[2] public maxDailySpendingNumDen;
uint256 public percentageToSendOnClaim;
uint256 public maxDailySpendingCap;
bool public isPaused;
address public stakingContractAddress;
uint256 public totalPointsIssued;
uint256 public remainingPoints;
  • maxDailySpendingNumDen: Maximum daily spending limit as a fraction.
  • percentageToSendOnClaim: Percentage of rewards to send immediately on claim.
  • maxDailySpendingCap: Maximum cap for daily spending.
  • isPaused: Flag to pause/unpause the contract.
  • stakingContractAddress: Address of the associated staking contract.
  • totalPointsIssued: Total number of points issued.
  • remainingPoints: Number of unspent/unclaimed points.

Events

event ActivityCreated(string name);
event ActivityClaimed(string name, address user, uint256 amount);
event AuthorizedAddressUpdated(address authorizedAddress, bool allowed);
event BalanceUpdated(address user, uint256 amount);
event ReferenceBalanceUpdated(address user, uint256 amount);
event MaxSpendingUpdated(uint256[2] maxSpendingNumDen);
event TokenSpent(address user, uint256 amount, uint256 dayId);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Deposited(address user, uint256 amount);
event Withdrawn(address user, uint256 amount);
event DepositedTokensSpent(address user, uint256 amount);

Each event captures important state changes or actions within the contract:

  • ActivityCreated: When a new activity is created.
  • ActivityClaimed: When a user claims rewards for an activity.
  • AuthorizedAddressUpdated: When an address's authorization status changes.
  • BalanceUpdated: When a user's balance changes.
  • ReferenceBalanceUpdated: When a user's reference balance changes.
  • MaxSpendingUpdated: When the maximum daily spending limit is updated.
  • TokenSpent: When tokens are spent by a user.
  • Approval: When a delegated spending approval is made.
  • Deposited: When a user deposits tokens.
  • Withdrawn: When a user withdraws tokens.
  • DepositedTokensSpent: When deposited tokens are spent.

Function Signatures and Descriptions

initialize

function initialize(address initialOwner) initializer public

Initializes the contract, setting up the initial owner and EIP712 domain separator.

createActivity

function createActivity(string memory name, uint256 lockupDays) public onlyOwner

Creates a new activity with a specified lockup period. Only the owner can create activities.

claimActivityRewards

function claimActivityRewards(
address user,
uint256[] memory amounts,
string[] memory names,
uint8 v,
bytes32 r,
bytes32 s
) nonReentrant public

Allows users to claim rewards for completed activities. Requires a signature from an admin for verification.

withdrawRewards

function withdrawRewards(string[] memory names) nonReentrant public

Allows users to withdraw their rewards for specified activities after the lockup period.

spendToken

function spendToken(
Request calldata request,
bytes calldata signature
) external nonReentrant returns(address spender)

Allows users to spend their tokens based on a signed request. Typically used for interacting with other contracts in the ecosystem.

approve

function approve(address spender, uint256 amount) external

Approves a spender to spend a certain amount of tokens on behalf of the owner.

spendTokensOnBehalf

function spendTokensOnBehalf(
DelegatedRequest calldata request,
bytes calldata signature
) external nonReentrant

Allows spending tokens on behalf of another user, based on a signed delegated request.

deposit

function deposit() public payable

Allows users to deposit Ether into their account, increasing their balance.

withdraw

function withdraw(uint256 amount) public

Allows users to withdraw Ether from their account, decreasing their balance.

setMaxDailySpending

function setMaxDailySpending(uint256[2] memory _maxDailySpendingNumDen) public onlyOwner

Sets the maximum daily spending limit.

updatePercentageToSendOnClaim

function updatePercentageToSendOnClaim(uint256 _percentageToSendOnClaim) public onlyOwner

Updates the percentage of rewards to send immediately on claim.

updateAdminAddresses

function updateAdminAddresses(address[] memory _adminAddresses, bool[] memory _allowed) public onlyOwner

Updates the admin status for multiple addresses.

pause

function pause() public onlyOwner

Pauses the contract.

unpause

function unpause() public onlyOwner

Unpauses the contract.

setStakingContractAddress

function setStakingContractAddress(address _stakingContractAddress) public onlyOwner

Sets the address of the staking contract.

getTotalBalance

function getTotalBalance(address user) public view returns(uint256)

Returns the total balance of a user, including staking points.

feesWithdrawableBy

function feesWithdrawableBy(address account) public view returns (uint256)

Calculates fees withdrawable by an account.

withdrawFees

function withdrawFees(address account) public

Withdraws fees for an account.

getAvailableSpending

function getAvailableSpending(address spender) public view returns(uint256)

Calculates the available spending for a user.

emergencyWithdraw

function emergencyWithdraw() external onlyOwner

Allows the owner to withdraw all balance in case of emergency.

Interaction with Other Contracts

  1. PredictionsOracle:

    • Calls spendToken or spendTokensOnBehalf when users buy positions using their OnchainPoints balance.
  2. Staking Contract:

    • Interacts with the staking contract to check earned staking points and potentially auto-stake rewards.

Key Features and Considerations

  1. Activity and Reward System: Users participate in activities and claim rewards, subject to lockup periods.
  2. Signature-Based Spending: Uses EIP712 signatures for secure, off-chain approval of spending requests.
  3. Delegated Spending: Users can approve other addresses to spend tokens on their behalf.
  4. Daily Spending Limits: Implements configurable daily spending limits.
  5. Pausable Functionality: Can be paused by the owner in emergencies.
  6. Upgradability: Designed to be upgradeable using the UUPS pattern.
  7. Integration with Staking: Can interact with a separate staking contract for complex reward mechanisms.

Usage Examples

Creating an Activity and Claiming Rewards

// Owner creates a new activity
onchainPoints.createActivity("Daily Login", 7 days);

// Admin signs a message approving a reward claim
bytes32 message = keccak256(abi.encodePacked(userAddress, [100], ["Daily Login"]));
(uint8 v, bytes32 r, bytes32 s) = signMessage(adminPrivateKey, message);

// User claims the reward
onchainPoints.claimActivityRewards(userAddress, [100], ["Daily Login"], v, r, s);

// After lockup period, user withdraws rewards
onchainPoints.withdrawRewards(["Daily Login"]);

Spending Tokens in Prediction Market

// User wants to spend 100 tokens to buy a position
uint256 deadline = block.timestamp + 1 hour;
string memory nonce = "uniqueNonce123";
uint256 amount = 100;

// Create and sign the request
OnchainPoints.Request memory request = OnchainPoints.Request(deadline, nonce, amount);
bytes memory signature = signRequest(userPrivateKey, request);

// PredictionsOracle contract calls OnchainPoints to spend tokens
onchainPoints.spendToken(request, signature);

// PredictionsOracle then uses these tokens to buy a position in the market