推荐系统
JAMM DEX 内置了一个创新的推荐奖励系统,通过智能合约自动执行费用分成,为推荐人提供持续的收益激励。这个系统不仅能够激励用户推广平台,还能降低交易者的实际交易成本。
推荐人注册机制
推荐人映射
JAMMFactory
合约维护了一个推荐人映射关系:
// from JAMMFactory.sol
mapping(address => address) public referrer;
这个映射记录了每个用户(通过 tx.origin
识别)对应的推荐人地址。
设置推荐人
用户可以通过调用 JAMMFactory
合约的 setReferrer
函数来设置推荐人:
// from JAMMFactory.sol
function setReferrer(address _referrer) external override {
require(referrer[tx.origin] == address(0), "JAMMFactory: REFERER_EXISTS");
referrer[tx.origin] = _referrer;
emit Referrer(tx.origin, _referrer);
}
重要特性:
每个用户只能设置一次推荐人。
使用
tx.origin
而不是msg.sender
,确保即使通过合约调用也能正确记录发起交易的真实用户。一旦设置就无法更改,保证推荐关系的稳定性。
推荐人事件
Referrer
事件在推荐关系建立时触发:
// from JAMMFactory.sol
event Referrer(address indexed sender, address indexed referrer);
这个事件记录了推荐关系的建立,便于前端应用和分析工具追踪。
费用分成机制
双重费用系统
JAMM DEX 的推荐人系统实现了独特的双重费率机制,在 JAMMPair.sol
的 _collectFee
函数中实现,根据是否有推荐人来决定费用分配:
// from JAMMPair.sol
function _collectFee(
uint amount0In,
uint amount1In,
address _referrer
) private {
address referrer = IJAMMFactory(factory).referrer(tx.origin);
address feeTo = IJAMMFactory(factory).feeTo();
if (_referrer == address(0) && referrer == address(0)) {
// Case 1: No referrer - all fees go to the protocol
_safeTransferFee(token0, feeTo, (amount0In * fee) / 50000);
_safeTransferFee(token1, feeTo, (amount1In * fee) / 50000);
} else {
// Case 2: With referrer - fees are split between protocol and referrer
_safeTransferFee(token0, feeTo, (amount0In * fee) / 100000);
_safeTransferFee(token1, feeTo, (amount1In * fee) / 100000);
if (referrer != address(0)) {
_safeTransferFee(token0, referrer, (amount0In * fee) / 100000);
_safeTransferFee(token1, referrer, (amount1In * fee) / 100000);
} else {
// If referrer is provided in swap, set it for tx.origin
IJAMMFactory(factory).setReferrer(_referrer);
_safeTransferFee(token0, _referrer, (amount0In * fee) / 100000);
_safeTransferFee(token1, _referrer, (amount1In * fee) / 100000);
}
}
}
费用分配详解
情况1:无推荐人
协议费用: 从输入代币中收取
(amountIn * fee) / 50000
。推荐人费用: 0。
情况2:有推荐人
协议费用: 从输入代币中收取
(amountIn * fee) / 100000
。推荐人费用: 从输入代币中收取
(amountIn * fee) / 100000
。费用在协议和推荐人之间平分。
推荐人设置的两种方式
方式1:预先设置
用户已经通过 setReferrer
函数设置了推荐人。在这种情况下,_collectFee
函数会使用 tx.origin
对应的已注册推荐人。
方式2:交易时设置
用户在交易时通过 _referrer
参数设置推荐人。如果 tx.origin
尚未设置推荐人,则此参数将用于设置。
推荐人收益计算
收益公式
推荐人的收益按以下公式计算:
单笔交易收益 = (交易输入金额 * 费率) / 100000
收益示例
以1%费率(fee = 100
)的交易对为例:
场景: 用户交换1000个TokenA
无推荐人时:
协议收取:
1000 * 100 / 50000 = 2
TokenA推荐人收取: 0
有推荐人时:
协议收取:
1000 * 100 / 100000 = 1
TokenA推荐人收取:
1000 * 100 / 100000 = 1
TokenA
多代币收益
由于JAMM DEX支持任意ERC-20代币交换,推荐人可能收到多种不同的代币作为奖励:
// from JAMMPair.sol (_collectFee function)
_safeTransferFee(token0, referrer, (amount0In * fee) / 100000);
_safeTransferFee(token1, referrer, (amount1In * fee) / 100000);
推荐人的收益组合取决于:
被推荐用户的交易习惯。
交易的代币种类。
交易的频率和金额。
推荐人系统的优势
对推荐人的好处
持续收益: 每次被推荐用户交易都能获得分成。
多样化收益: 可能收到多种代币奖励。
无需质押: 不需要锁定任何资金。
自动执行: 智能合约自动分配,无需手动操作。
对用户的好处
降低成本: 虽然总费率相同,但有推荐人分成的激励。
支持推荐人: 帮助推荐人获得收益。
一次设置: 推荐关系永久有效。
对协议的好处
用户增长: 激励用户推广平台。
网络效应: 推荐关系形成用户网络。
费用优化: 通过分成机制优化费用结构。
技术实现细节
安全考虑
使用 tx.origin
// from JAMMFactory.sol (referrer mapping)
address referrer = IJAMMFactory(factory).referrer(tx.origin);
使用 tx.origin
而不是 msg.sender
的原因:
确保即使通过Router合约调用,也能正确识别发起交易的真实用户。
防止合约代理导致的推荐关系错误。
一次性设置
// from JAMMFactory.sol (setReferrer function)
require(referrer[tx.origin] == address(0), "JAMMFactory: REFERER_EXISTS");
这个检查确保:
每个用户只能设置一次推荐人。
防止推荐关系被恶意修改。
保证推荐人收益的稳定性。
费用转账安全
// from JAMMPair.sol
function _safeTransferFee(address token, address to, uint value) private {
_safeTransfer(token, to, value);
if (value > 0) {
emit Fee(tx.origin, to, token, value);
}
}
安全转账函数确保:
使用安全的代币转账方法。
记录费用转账事件。
处理零金额情况。
集成指南
前端集成
检查推荐人状态
const factory = new ethers.Contract(factoryAddress, factoryABI, provider);
const currentReferrer = await factory.referrer(userAddress);
if (currentReferrer === ethers.constants.AddressZero) {
console.log("User has not set a referrer.");
} else {
console.log("Current referrer:", currentReferrer);
}
设置推荐人
// Method 1: Direct call to Factory contract
const tx = await factory.setReferrer(referrerAddress);
await tx.wait();
// Method 2: Set during a swap (via Router)
const tx = await router.swapExactTokensForTokens(
amountIn,
amountOutMin,
path,
fees,
to,
referrerAddress, // Referrer address
deadline
);
推荐人收益查询
监听Fee事件
const pairContract = new ethers.Contract(pairAddress, pairABI, provider);
// Listen for fees for a specific referrer
const filter = pairContract.filters.Fee(null, referrerAddress);
pairContract.on(filter, (sender, referrer, token, amount, event) => {
console.log(`Referrer ${referrer} received ${ethers.utils.formatEther(amount)} of ${token}`);
});
计算历史收益
// Query historical Fee events
const events = await pairContract.queryFilter(
pairContract.filters.Fee(null, referrerAddress),
fromBlock,
toBlock
);
let totalRewards = {};
events.forEach(event => {
const { token, amount } = event.args;
if (!totalRewards[token]) {
totalRewards[token] = ethers.BigNumber.from(0);
}
totalRewards[token] = totalRewards[token].add(amount);
});
Last updated