The on-chain component of Opentip. Written in Solidity, deployed on Base.
Mainnet (Base)
Not yet deployed
Solidity
^0.8.26
License
MIT
Lines
298
USDC (Testnet)
0x036CbD53842c5426634e7929541eC2318f3dCF7eThe contract inherits from four OpenZeppelin libraries:
Ownable2Step
Two-step ownership transfer for safety
Pausable
Emergency pause/unpause mechanism
ReentrancyGuard
Protection against reentrancy attacks
EIP712
Off-chain typed signature verification
| Constant | Value | Description |
|---|---|---|
| MIN_TIP | 1e6 | Minimum tip: 1 USDC ($1) |
| MAX_FEE_BPS | 1000 | Maximum fee: 10% |
| MAX_REPO_ID_LENGTH | 200 | Maximum characters for a repo ID |
| Function | Parameters | Returns | Modifier | Description |
|---|---|---|---|---|
| registerRepo | repoId, payoutAddress, expiry, nonce, signature | — | whenNotPaused | Register a repo with an EIP-712 signed permit. Validates format, expiry, and signature. |
| updatePayoutAddress | repoId, newAddress | — | whenNotPaused | Rotate the payout wallet for a registered repo. Only callable by current payout address. |
| receiveTip | repoId, usdcAmount | — | nonReentrant, whenNotPaused | Accept a USDC tip. Pulls USDC from msg.sender, splits fee, credits pending balance. |
| claim | repoId | — | nonReentrant, whenNotPaused | Withdraw all pending USDC for a repo. Only callable by the payout address. |
| Function | Parameters | Returns | Modifier | Description |
|---|---|---|---|---|
| pause | — | — | onlyOwner | Emergency pause. Blocks register, update, tip, and claim. |
| unpause | — | — | onlyOwner | Resume operations after a pause. |
| setFeeBps | newFeeBps | — | onlyOwner | Update platform fee. Capped at MAX_FEE_BPS (1000 = 10%). |
| setTreasuryAddress | newTreasury | — | onlyOwner | Change the treasury address. Migratable to Gnosis Safe. |
| withdrawTreasury | amount | — | onlyOwner, nonReentrant | Withdraw accumulated fees to the treasury address. |
| setRegistrarSigner | newSigner | — | onlyOwner | Rotate the EIP-712 registrar signer. Old key is immediately invalidated. |
| adminReassignPayout | repoId, newAddress | — | onlyOwner | Emergency recovery: reassign payout address if wallet key is lost. |
| Function | Parameters | Returns | Modifier | Description |
|---|---|---|---|---|
| getPendingBalance | repoId | uint256 | view | USDC available to claim for a repo. |
| getPayoutAddress | repoId | address | view | The wallet that can claim tips for a repo. |
| getTotalTipped | repoId | uint256 | view | Lifetime gross tip amount for a repo. |
| getTotalTipCount | repoId | uint256 | view | Lifetime tip count for a repo. |
| getFeeBps | — | uint256 | view | Current platform fee in basis points. |
| getTreasuryBalance | — | uint256 | view | Accumulated unwithdrawn fees. |
| isRegistered | repoId | bool | view | Whether a repo has been registered. |
| Event | Parameters | Description |
|---|---|---|
| RepoRegistered | repoId, payoutAddress (indexed), timestamp | Emitted when a new repo is registered. |
| PayoutAddressUpdated | repoId, oldAddress, newAddress (indexed) | Emitted when a payout wallet is rotated. |
| TipReceived | tipper (indexed), repoId, usdcAmount, feeAmount, timestamp | Emitted on every tip. |
| Claimed | payoutAddress (indexed), repoId, amount, timestamp | Emitted when a developer claims tips. |
| TreasuryWithdrawn | to (indexed), amount, timestamp | Emitted when treasury funds are withdrawn. |
| FeeUpdated | oldFeeBps, newFeeBps | Emitted when the platform fee changes. |
| TreasuryAddressUpdated | oldTreasury (indexed), newTreasury (indexed) | Emitted when the treasury address changes. |
| RegistrarSignerUpdated | oldSigner (indexed), newSigner (indexed) | Emitted when the registrar signer is rotated. |
| AdminPayoutReassigned | repoId, oldAddress, newAddress, admin (indexed) | Emitted when an admin reassigns a payout address. |
The platform fee is expressed in basis points (bps). 500 bps = 5%. The fee is capped at 1000 bps (10%) and enforced in both the constructor and setFeeBps.
receiveTip()usdcAmount * feeBps / 10000treasuryBalancependingBalanceclaim() to withdraw their sharewithdrawTreasury() to collect feesRegistration uses EIP-712 typed signatures to prevent squatting. The process:
Register typed struct with the registrar keyregisterRepo()/ separator/{
name: "Opentip",
version: "1",
chainId: 84532,
verifyingContract: "0xeD13dB8234d437771e115419BF7498Ddef90Dc8D"
}All state-changing external functions (receiveTip, claim, withdrawTreasury) use nonReentrant.
The owner can pause the contract in an emergency. Paused contract blocks registration, tip updates, tips, and claims.
Uses Ownable2Step — ownership transfer requires the new owner to accept. Prevents accidental transfer to a wrong address.
All functions update state before making external calls (USDC transfers), following the CEI pattern.
The contract rejects tips below 1 USDC (MIN_TIP = 1e6) to prevent dust attacks.
The full contract source is available at contracts/src/Opentip.sol in the repository.