推荐系统

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);

推荐人的收益组合取决于:

  • 被推荐用户的交易习惯。

  • 交易的代币种类。

  • 交易的频率和金额。

推荐人系统的优势

对推荐人的好处

  1. 持续收益: 每次被推荐用户交易都能获得分成。

  2. 多样化收益: 可能收到多种代币奖励。

  3. 无需质押: 不需要锁定任何资金。

  4. 自动执行: 智能合约自动分配,无需手动操作。

对用户的好处

  1. 降低成本: 虽然总费率相同,但有推荐人分成的激励。

  2. 支持推荐人: 帮助推荐人获得收益。

  3. 一次设置: 推荐关系永久有效。

对协议的好处

  1. 用户增长: 激励用户推广平台。

  2. 网络效应: 推荐关系形成用户网络。

  3. 费用优化: 通过分成机制优化费用结构。

技术实现细节

安全考虑

使用 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