Skip to content

Commit 4a8c490

Browse files
Use eth for payments and add discounts
1 parent 3837851 commit 4a8c490

7 files changed

Lines changed: 444 additions & 166 deletions

File tree

contracts/talent_plus/README.md

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This directory contains the core smart contracts for the TalentPlus subscription
44

55
## Overview
66

7-
The TalentPlus system enables trusted signers to purchase subscriptions for users using TALENT tokens, with flexible subscription models and administrative capabilities for custom subscription management. Trusted signers can purchase subscriptions for any wallet address, enabling gift subscriptions and corporate subscription management.
7+
The TalentPlus system enables trusted signers to purchase subscriptions for users using ETH payments, with flexible subscription models, TALENT token-based discounts (including vault staking), and administrative capabilities for custom subscription management. Trusted signers can purchase subscriptions for any wallet address, enabling gift subscriptions and corporate subscription management.
88

99
## Contracts
1010

@@ -15,6 +15,8 @@ The core subscription management contract that handles subscription models and u
1515
#### Key Features
1616

1717
- **Subscription Model Management**: Create, update, and deactivate subscription models
18+
- **TALENT Token Integration**: Check TALENT token balances and vault staking for discount eligibility
19+
- **Dynamic Discount System**: Apply discounts based on combined TALENT holdings (balance + vault staking)
1820
- **User Subscription Management**: Add, upgrade, and extend user subscriptions
1921
- **Custom Expiration Support**: Administrative function to set custom expiration times
2022
- **Access Control**: Owner and trusted signer permissions
@@ -23,8 +25,8 @@ The core subscription management contract that handles subscription models and u
2325
#### Main Functions
2426

2527
**Subscription Model Management:**
26-
- `addSubscriptionModel(string subscriptionSlug, uint256 durationInSeconds, uint256 priceInTalent)`
27-
- `updateSubscriptionModel(string subscriptionSlug, uint256 durationInSeconds, uint256 priceInTalent)`
28+
- `addSubscriptionModel(string subscriptionSlug, uint256 durationInSeconds, uint256 priceInEth, uint256 discountPercentage, uint256 talentRequiredForDiscount)`
29+
- `updateSubscriptionModel(string subscriptionSlug, uint256 durationInSeconds, uint256 priceInEth, uint256 discountPercentage, uint256 talentRequiredForDiscount)`
2830
- `deactivateSubscriptionModel(string subscriptionSlug)`
2931

3032
**User Subscription Management:**
@@ -37,6 +39,8 @@ The core subscription management contract that handles subscription models and u
3739
- `getCurrentActiveSubscription(address wallet)`
3840
- `getSubscriptionExpiration(address wallet)`
3941
- `getSubscriptionStartTime(address wallet)`
42+
- `getSubscriptionModel(string subscriptionSlug)` - Returns model details including discount info
43+
- `calculateDiscountedPrice(string subscriptionSlug, address wallet)` - Calculates final price with discount applied based on TALENT balance + vault staking
4044

4145
**Access Control:**
4246
- `addTrustedSigner(address signer)`
@@ -62,32 +66,33 @@ struct UserActiveSubscription {
6266

6367
### 2. TalentPlus.sol
6468

65-
The payment and subscription creation contract that handles TALENT token payments and integrates with TalentPlusSubscription. Only trusted signers can call the subscription functions.
69+
The payment and subscription creation contract that handles ETH payments and integrates with TalentPlusSubscription. Only trusted signers can call the subscription functions.
6670

6771
#### Key Features
6872

69-
- **TALENT Token Integration**: Handles ERC20 token payments for subscriptions
73+
- **ETH Payment Integration**: Handles native ETH payments for subscriptions
74+
- **TALENT-Based Discounts**: Automatically applies discounts based on TALENT token holdings and vault staking
7075
- **Direct Access Control**: Only trusted signers can create subscriptions
71-
- **Dynamic Pricing**: Fetches subscription costs from TalentPlusSubscription contract
76+
- **Dynamic Pricing**: Fetches subscription costs and applies discounts from TalentPlusSubscription contract
7277
- **Gift Subscriptions**: Trusted signers can purchase subscriptions for any wallet address
7378
- **Administrative Control**: Owner-managed contract settings
7479
- **Integration**: Seamless integration with TalentPlusSubscription
7580

7681
#### Main Functions
7782

7883
**Core Subscription:**
79-
- `subscribe(address wallet, string subscriptionSlug)` - Main subscription function (trusted signers can purchase for any wallet)
84+
- `subscribe(address wallet, string subscriptionSlug)` - Main subscription function (trusted signers can purchase for any wallet, requires ETH payment)
8085

8186
**Administrative:**
8287
- `setEnabled(bool _enabled)` - Enable/disable contract
8388
- `setDisabled()` - Disable contract
84-
- `updateReceiver(address _feeReceiver)` - Update fee receiver address
89+
- `updateReceiver(address _feeReceiver)` - Update ETH fee receiver address
8590
- `updateTalentPlusSubscription(address _talentPlusSubscriptionAddress)` - Update subscription contract address
8691

8792
#### Events
8893

8994
```solidity
90-
event SubscriptionCreated(address indexed payer, address indexed recipient, string subscriptionSlug);
95+
event SubscriptionCreated(address indexed payer, address indexed recipient, string subscriptionSlug, uint256 finalPrice, bool discountApplied);
9196
```
9297

9398
## Integration Flow
@@ -102,14 +107,19 @@ sequenceDiagram
102107
participant TALENT_Token
103108
participant FeeReceiver
104109
105-
TrustedSigner->>TalentPlus: subscribe(wallet, slug)
110+
TrustedSigner->>TalentPlus: subscribe(wallet, slug) + ETH
106111
TalentPlus->>TalentPlusSubscription: getSubscriptionModel(slug)
107-
TalentPlusSubscription-->>TalentPlus: (duration, price, active)
112+
TalentPlusSubscription-->>TalentPlus: (duration, price, discount%, talentRequired, active)
113+
TalentPlus->>TalentPlusSubscription: calculateDiscountedPrice(slug, wallet)
114+
TalentPlusSubscription->>TALENT_Token: balanceOf(wallet)
115+
TALENT_Token-->>TalentPlusSubscription: balance
116+
TalentPlusSubscription-->>TalentPlus: (finalPrice, discountApplied)
108117
TalentPlus->>TalentPlus: verify msg.sender == trustedSigner
109-
TalentPlus->>TALENT_Token: transferFrom(trustedSigner, feeReceiver, price)
118+
TalentPlus->>TalentPlus: verify msg.value >= finalPrice
119+
TalentPlus->>FeeReceiver: transfer ETH
110120
TalentPlus->>TalentPlusSubscription: addUserSubscription(wallet, slug)
111121
TalentPlusSubscription-->>TalentPlus: success
112-
TalentPlus->>TalentPlus: emit SubscriptionCreated(trustedSigner, wallet, slug)
122+
TalentPlus->>TalentPlus: emit SubscriptionCreated(trustedSigner, wallet, slug, finalPrice, discountApplied)
113123
```
114124

115125
### 2. Custom Subscription Flow
@@ -152,11 +162,37 @@ sequenceDiagram
152162
### Basic Subscription Purchase
153163

154164
```solidity
155-
// Trusted signer purchases subscription for a user
156-
talentPlus.subscribe(userWallet, "premium");
165+
// Trusted signer purchases subscription for a user (full price)
166+
talentPlus.subscribe(userWallet, "premium", { value: parseEther("100") });
157167
158-
// Trusted signer purchases subscription as a gift for another user
159-
talentPlus.subscribe(recipientWallet, "premium");
168+
// Trusted signer purchases subscription as a gift for another user (with discount)
169+
// User has 5000 TALENT tokens, so gets 20% discount on premium subscription
170+
talentPlus.subscribe(recipientWallet, "premium", { value: parseEther("80") }); // 100 - 20% = 80 ETH
171+
```
172+
173+
### Discount System Management (Admin)
174+
175+
```solidity
176+
// Admin creates subscription model with discount
177+
talentPlusSubscription.addSubscriptionModel(
178+
"premium", // subscription slug
179+
90 * 24 * 60 * 60, // 90 days duration
180+
parseEther("100"), // 100 ETH base price
181+
20, // 20% discount
182+
parseEther("5000") // 5000 TALENT tokens required for discount
183+
);
184+
185+
// Admin updates discount parameters
186+
talentPlusSubscription.updateSubscriptionModel(
187+
"premium",
188+
90 * 24 * 60 * 60,
189+
parseEther("100"),
190+
25, // Updated to 25% discount
191+
parseEther("10000") // Updated to 10000 TALENT tokens required
192+
);
193+
194+
// Check discounted price for a specific wallet
195+
(uint256 finalPrice, bool discountApplied) = talentPlusSubscription.calculateDiscountedPrice("premium", userWallet);
160196
```
161197

162198
### Custom Subscription Creation (Admin)
@@ -251,15 +287,44 @@ npx hardhat test test/contracts/talent_plus/
251287
- **Corporate Subscriptions**: Companies can manage subscriptions for their employees
252288
- **Promotional Subscriptions**: Marketing teams can give away subscriptions
253289
- **Admin-Managed Subscriptions**: Administrators can create subscriptions for users
290+
- **TALENT Holder Rewards**: Users with TALENT tokens or vault staking get automatic discounts
291+
- **Tiered Pricing**: Different discount levels based on combined TALENT holdings (balance + vault staking)
292+
293+
### Discount System Benefits
294+
- **Automatic Discounts**: No manual intervention required - discounts applied automatically
295+
- **TALENT Token Utility**: Increases value and utility of TALENT tokens and vault staking
296+
- **Flexible Configuration**: Admins can adjust discount percentages and requirements
297+
- **Transparent Pricing**: Users can check their discounted price before purchasing
298+
- **Fair Access**: Discounts based on actual token holdings and vault staking, not arbitrary criteria
299+
300+
## Vault Integration
301+
302+
The TalentPlus system integrates with the Talent Vault contract to provide enhanced discount eligibility based on staked TALENT tokens.
303+
304+
### Vault Address
305+
- **Mainnet**: `0x23Ff3256A29847d7EF760943bd6679b565CbdE5a`
306+
- **Testnet**: `0x23Ff3256A29847d7EF760943bd6679b565CbdE5a` (same address)
307+
308+
### How Vault Staking Works
309+
1. **Combined Holdings**: Discount eligibility is calculated using `TALENT balance + vault staked amount`
310+
2. **Automatic Detection**: The system automatically checks both wallet balance and vault staking
311+
3. **Seamless Integration**: Users don't need to do anything special - staking automatically qualifies them for discounts
312+
4. **Flexible Requirements**: Admins can set discount thresholds that consider both sources of TALENT holdings
313+
314+
### Example Scenarios
315+
- **Scenario 1**: User has 500 TALENT in wallet + 500 TALENT staked in vault = 1000 TALENT total → qualifies for discount
316+
- **Scenario 2**: User has 0 TALENT in wallet + 1000 TALENT staked in vault = 1000 TALENT total → qualifies for discount
317+
- **Scenario 3**: User has 1000 TALENT in wallet + 0 TALENT staked in vault = 1000 TALENT total → qualifies for discount
254318

255319
## Dependencies
256320

257-
- **OpenZeppelin Contracts**: `Ownable`, `ReentrancyGuard`, `IERC20`, `SafeERC20`
321+
- **OpenZeppelin Contracts**: `Ownable`, `ReentrancyGuard`, `IERC20`
258322
- **Solidity**: `^0.8.24`
259323

260324
## Network Configuration
261325

262326
The contracts support both mainnet and testnet deployments with network-specific configurations for:
263327
- TALENT token addresses
328+
- Vault contract addresses
264329
- Fee receiver addresses
265330
- Trusted signer addresses

contracts/talent_plus/TalentPlus.sol

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,28 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.24;
33

4-
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5-
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
64
import "./TalentPlusSubscription.sol";
75
import "@openzeppelin/contracts/access/Ownable.sol";
86
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
97

108
contract TalentPlus is Ownable, ReentrancyGuard {
11-
using SafeERC20 for IERC20;
12-
13-
// TALENT token address
14-
IERC20 public immutable TALENT_TOKEN;
159

1610
address public trustedSigner;
1711
address public feeReceiver;
1812
TalentPlusSubscription public talentPlusSubscription;
1913

20-
event SubscriptionCreated(address indexed payer, address indexed recipient, string subscriptionSlug);
14+
event SubscriptionCreated(address indexed payer, address indexed recipient, string subscriptionSlug, uint256 finalPrice, bool discountApplied);
2115

2216
bool public enabled;
2317

2418
constructor(
2519
address _trustedSigner,
2620
address _talentPlusSubscriptionAddress,
27-
address _feeReceiver,
28-
address _talentTokenAddress
21+
address _feeReceiver
2922
) Ownable(msg.sender) {
3023
trustedSigner = _trustedSigner;
3124
talentPlusSubscription = TalentPlusSubscription(_talentPlusSubscriptionAddress);
3225
feeReceiver = _feeReceiver;
33-
TALENT_TOKEN = IERC20(_talentTokenAddress);
3426
enabled = true;
3527
}
3628

@@ -75,23 +67,35 @@ contract TalentPlus is Ownable, ReentrancyGuard {
7567
* @param wallet The wallet address to create the subscription for.
7668
* @param subscriptionSlug The subscription slug to set in TalentPlusSubscription.
7769
* @dev Only the trusted signer can call this function. TalentPlus contract is a trusted signer in TalentPlusSubscription.
70+
* @dev Requires ETH payment equal to the subscription cost.
7871
*/
79-
function subscribe(address wallet, string memory subscriptionSlug) public nonReentrant {
72+
function subscribe(address wallet, string memory subscriptionSlug) public payable nonReentrant {
8073
require(enabled, "Subscription is disabled for this contract");
8174
require(wallet != address(0), "Invalid wallet address");
8275
require(bytes(subscriptionSlug).length > 0, "Subscription slug cannot be empty");
8376
require(msg.sender == trustedSigner, "Only trusted signer can create subscriptions");
8477

85-
// Get the subscription model details including price
86-
(, uint256 subscriptionCost, bool isActive) = talentPlusSubscription.getSubscriptionModel(subscriptionSlug);
78+
// Get the subscription model details and calculate discounted price
79+
(, , , , bool isActive) = talentPlusSubscription.getSubscriptionModel(subscriptionSlug);
8780
require(isActive, "Subscription model is not active");
81+
82+
// Calculate discounted price based on TALENT holdings
83+
(uint256 finalPrice, bool discountApplied,) = talentPlusSubscription.calculateDiscountedPrice(subscriptionSlug, wallet);
84+
require(msg.value >= finalPrice, "Insufficient ETH payment");
85+
86+
// Transfer ETH to fee receiver
87+
(bool success, ) = feeReceiver.call{value: finalPrice}("");
88+
require(success, "ETH transfer failed");
8889

89-
// Transfer TALENT tokens from trusted signer to fee receiver
90-
TALENT_TOKEN.safeTransferFrom(msg.sender, feeReceiver, subscriptionCost);
90+
// Refund excess ETH if any
91+
if (msg.value > finalPrice) {
92+
(bool refundSuccess, ) = msg.sender.call{value: msg.value - finalPrice}("");
93+
require(refundSuccess, "ETH refund failed");
94+
}
9195

9296
// Set the subscription for the target wallet in TalentPlusSubscription
9397
talentPlusSubscription.addUserSubscription(wallet, subscriptionSlug);
9498

95-
emit SubscriptionCreated(msg.sender, wallet, subscriptionSlug);
99+
emit SubscriptionCreated(msg.sender, wallet, subscriptionSlug, finalPrice, discountApplied);
96100
}
97101
}

0 commit comments

Comments
 (0)