如何在智能合约中利用Oracle返回的数组实现五级推荐奖励机制?
五级推荐奖励质押DApp的实现方案
整体逻辑概览
整个流程分为四个核心环节:
- 用户前端提交推荐关系到后端数据库
- 用户质押时,智能合约通过Oracle发起链外数据请求
- 后端API处理请求,从数据库提取五级推荐链数据
- Oracle将数据返回合约,合约按比例计算并发放推荐奖励
分步实现细节
1. 前端与数据库的推荐关系存储
- 用户在质押页面填写推荐人地址(可选),点击质押后,前端组装指定格式的JSON数据发送到后端API:
{ "type": "register", "address":"0x78910", "referrer":"0x12345", "coin":20 }
- 后端接收数据后,在数据库中维护推荐链关系:建议创建
referral_links表,字段包括user_address、referrer_address。也可以提前计算并存储每个用户的五级推荐人(递归遍历上级推荐链,直到L5或无上级),这样后续Oracle查询效率更高。
2. 智能合约侧的Oracle集成
以Chainlink Oracle为例(核心逻辑通用,适配其他Oracle只需调整接口):
(1)合约基础结构
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract StakingReferral is ChainlinkClient { address public immutable rewardToken; // 奖励用的ERC20代币地址 address private oracle; bytes32 private jobId; uint256 private constant LINK_FEE = 0.1 * 10 ** 18; // 奖励比例:放大1000倍避免小数(7% = 70) uint256[5] public referralRates = [70, 30, 20, 10, 5]; // 记录用户质押金额(示例) mapping(address => uint256) public stakedAmounts; event ReferralRewardsPaid(address indexed staker, uint256 totalRewards); event OracleRequestInitiated(address indexed staker); constructor(address _rewardToken, address _oracle, bytes32 _jobId) { setPublicChainlinkToken(); rewardToken = _rewardToken; oracle = _oracle; jobId = _jobId; } }
(2)触发Oracle请求的质押函数
用户质押时,先处理基础质押逻辑,再发起Oracle请求获取推荐链:
function stake(uint256 amount) external { // 基础质押逻辑:锁定用户的ERC20代币 IERC20(rewardToken).transferFrom(msg.sender, address(this), amount); stakedAmounts[msg.sender] += amount; // 构建Oracle请求,传递用户地址给后端API Chainlink.Request memory req = buildChainlinkRequest( jobId, address(this), this.fulfillReferralData.selector ); req.add("get", string(abi.encodePacked("https://your-api-domain.com/referral-chain?addr=", msg.sender))); req.add("pathReferrers", "referrers"); // 指定JSON中推荐人数组的路径 req.add("pathLevels", "levels"); // 指定JSON中层级数组的路径 sendChainlinkRequestTo(oracle, req, LINK_FEE); emit OracleRequestInitiated(msg.sender); }
(3)Oracle回调与奖励发放
Oracle返回数据后,合约计算并发放奖励:
function fulfillReferralData( bytes32 _requestId, address[] memory _referrers, uint256[] memory _levels ) public recordChainlinkFulfillment(_requestId) { require(_referrers.length == _levels.length && _referrers.length <=5, "Invalid referral data"); uint256 stakeAmount = stakedAmounts[msg.sender]; uint256 totalPaid = 0; for(uint i=0; i<_referrers.length; i++){ uint256 rate = referralRates[_levels[i]-1]; uint256 reward = (stakeAmount * rate) / 1000; // 直接发放奖励,或存入奖励池待提取 IERC20(rewardToken).transfer(_referrers[i], reward); totalPaid += reward; } emit ReferralRewardsPaid(msg.sender, totalPaid); }
3. 后端API与Oracle Job配置
(1)API接口实现(Node.js/Express示例)
提供接口查询用户的五级推荐链:
const express = require('express'); const app = express(); const { Pool } = require('pg'); // 以PostgreSQL为例 const pool = new Pool({ /* 数据库连接配置 */ }); app.get('/referral-chain', async (req, res) => { const userAddr = req.query.addr; const referralChain = []; let currentAddr = userAddr; let level = 1; while(level <=5){ const result = await pool.query( 'SELECT referrer_address FROM referral_links WHERE user_address = $1', [currentAddr] ); if(result.rows.length ===0) break; const referrer = result.rows[0].referrer_address; referralChain.push({ addr: referrer, lvl: level }); currentAddr = referrer; level++; } // 返回Oracle需要的格式 res.json({ referrers: referralChain.map(item => item.addr), levels: referralChain.map(item => item.lvl) }); }); app.listen(3000, () => console.log('API running on port 3000'));
(2)Oracle Job配置
在Oracle节点上创建HTTP GET类型的Job,配置:
- 请求URL:你的API地址(带用户地址参数)
- 解析规则:从返回的JSON中提取
referrers和levels数组 - 回调合约:你的质押合约地址及
fulfillReferralData函数签名
4. 核心注意事项
- 数据校验:前端提交推荐人时,要验证地址格式合法性,后端也要校验推荐人是否为已注册用户,避免无效推荐链。
- Oracle安全:API接口需限制访问来源(比如Oracle节点IP白名单),防止恶意请求篡改推荐数据。
- Gas优化:可以将推荐奖励存入用户的奖励池,待用户提取质押代币时统一发放,减少链上交易次数。
- 精度处理:Solidity不支持小数,所有比例计算需放大倍数(如1000倍),计算完成后再还原。
内容的提问来源于stack exchange,提问作者Nirban Chakraborty
相关产品推荐
相关产品推荐

