WJU Wrapped Token

Overview

WJU (Wrapped JU) is the ERC-20 wrapped version of JuChain's native token JU. Since AMM systems require all tokens to follow the ERC-20 standard, the WJU contract provides seamless conversion functionality between JU tokens and ERC-20 tokens, enabling JU tokens to participate in all JAMM DEX trading activities.

Contract Implementation

Basic Information

contract WJU is IWJU {
    string public constant override name = "Wrapped JU";
    string public constant override symbol = "WJU";
    uint8 public constant override decimals = 18;
    uint public override totalSupply;
    mapping(address => uint) public override balanceOf;
    mapping(address => mapping(address => uint)) public override allowance;
}

The WJU contract fully implements the ERC-20 standard with the following characteristics:

  • Name: "Wrapped JU"

  • Symbol: "WJU"

  • Decimals: 18 decimal places

  • 1:1 Exchange: 1 JU = 1 WJU

Core Functions

Wrapping Function (Deposit)

Users can wrap JU tokens into WJU through two methods:

Method 1: Direct JU Transfer

receive() external payable {
    deposit();
}

Users directly send JU tokens to the WJU contract address, and the contract automatically calls the deposit() function.

Method 2: Call deposit Function

function deposit() public payable override {
    balanceOf[msg.sender] += msg.value;
    totalSupply += msg.value;
    emit Deposit(msg.sender, msg.value);
}

Wrapping Process:

  1. User sends JU tokens to the contract

  2. Contract increases user's WJU balance

  3. Increases WJU total supply

  4. Triggers Deposit event

Unwrapping Function (Withdraw)

Users can convert WJU tokens back to JU tokens:

function withdraw(uint amount) public override {
    require(balanceOf[msg.sender] >= amount);
    balanceOf[msg.sender] -= amount;
    payable(msg.sender).transfer(amount);
    emit Withdrawal(msg.sender, amount);
}

Unwrapping Process:

  1. Check if user has sufficient WJU balance

  2. Decrease user's WJU balance

  3. Transfer corresponding amount of JU tokens to user

  4. Trigger Withdrawal event

ERC-20 Standard Implementation

Transfer Function

function transfer(address to, uint amount) public override returns (bool) {
    require(balanceOf[msg.sender] >= amount, "WJU: INSUFFICIENT_BALANCE");
    balanceOf[msg.sender] -= amount;
    balanceOf[to] += amount;
    emit Transfer(msg.sender, to, amount);
    return true;
}

Authorized Transfer Function

function transferFrom(
    address from,
    address to,
    uint amount
) public override returns (bool) {
    require(
        allowance[from][msg.sender] >= amount,
        "WJU: INSUFFICIENT_ALLOWANCE"
    );
    require(balanceOf[from] >= amount, "WJU: INSUFFICIENT_BALANCE");
    allowance[from][msg.sender] -= amount;
    balanceOf[from] -= amount;
    balanceOf[to] += amount;
    emit Transfer(from, to, amount);
    return true;
}

Approval Function

function approve(
    address spender,
    uint amount
) public override returns (bool) {
    allowance[msg.sender][spender] = amount;
    emit Approval(msg.sender, spender, amount);
    return true;
}

Event System

Deposit Event

event Deposit(address indexed sender, uint amount);

Records JU token wrapping into WJU operations.

Withdrawal Event

event Withdrawal(address indexed sender, uint amount);

Records WJU token unwrapping into JU operations.

ERC-20 Standard Events

event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);

Application in JAMM DEX

Router Integration

The JAMMRouter contract is deeply integrated with WJU, providing convenient JU token trading functionality:

address public immutable override WJU;

constructor(address _factory, address _WJU) {
    factory = _factory;
    WJU = _WJU;
}

receive() external payable {
    assert(msg.sender == WJU); // only accept ETH from WJU contract
}

JU Token Trading

Buy Tokens with JU

function swapExactETHForTokens(
    uint amountOutMin,
    address[] calldata path,
    uint24[] calldata fees,
    address to,
    address referrer,
    uint deadline
) external payable virtual override ensure(deadline) returns (uint[] memory amounts) {
    require(path[0] == WJU, "JAMMRouter: INVALID_PATH");
    amounts = JAMMLibrary.getAmountsOut(factory, msg.value, path, fees);
    require(
        amounts[amounts.length - 1] >= amountOutMin,
        "JAMMRouter: INSUFFICIENT_OUTPUT_AMOUNT"
    );
    IWJU(WJU).deposit{value: amounts[0]}();
    assert(
        IWJU(WJU).transfer(
            JAMMLibrary.pairFor(factory, path[0], path[1], fees[0]),
            amounts[0]
        )
    );
    _swap(amounts, path, fees, to, referrer);
}

Trading Flow:

  1. User sends JU tokens

  2. Router wraps JU into WJU

  3. Transfer WJU to trading pair

  4. Execute swap logic

  5. User receives target tokens

Sell Tokens for JU

function swapExactTokensForETH(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    uint24[] calldata fees,
    address to,
    address referrer,
    uint deadline
) external virtual override ensure(deadline) returns (uint[] memory amounts) {
    require(path[path.length - 1] == WJU, "JAMMRouter: INVALID_PATH");
    amounts = JAMMLibrary.getAmountsOut(factory, amountIn, path, fees);
    require(
        amounts[amounts.length - 1] >= amountOutMin,
        "JAMMRouter: INSUFFICIENT_OUTPUT_AMOUNT"
    );
    TransferHelper.safeTransferFrom(
        path[0],
        msg.sender,
        JAMMLibrary.pairFor(factory, path[0], path[1], fees[0]),
        amounts[0]
    );
    _swap(amounts, path, fees, address(this), referrer);
    IWJU(WJU).withdraw(amounts[amounts.length - 1]);
    TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}

Trading Flow:

  1. User approves and transfers tokens

  2. Execute swap to get WJU

  3. Router unwraps WJU into JU

  4. Transfer JU tokens to user

Liquidity Management

Add JU Liquidity

function addLiquidityETH(
    address token,
    uint24 fee,
    uint amountTokenDesired,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
) external payable virtual override ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
    (amountToken, amountETH) = _addLiquidity(
        token,
        WJU,
        fee,
        amountTokenDesired,
        msg.value,
        amountTokenMin,
        amountETHMin
    );
    address pair = JAMMLibrary.pairFor(factory, token, WJU, fee);
    TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
    IWJU(WJU).deposit{value: amountETH}();
    assert(IWJU(WJU).transfer(pair, amountETH));
    liquidity = IJAMMPair(pair).mint(to);
    // refund excess JU
    if (msg.value > amountETH)
        TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
}

Remove JU Liquidity

function removeLiquidityETH(
    address token,
    uint24 fee,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
    (amountToken, amountETH) = removeLiquidity(
        token,
        WJU,
        fee,
        liquidity,
        amountTokenMin,
        amountETHMin,
        address(this),
        deadline
    );
    TransferHelper.safeTransfer(token, to, amountToken);
    IWJU(WJU).withdraw(amountETH);
    TransferHelper.safeTransferETH(to, amountETH);
}

Usage Examples

Basic Wrapping/Unwrapping

const wju = new ethers.Contract(WJU_ADDRESS, wjuABI, signer);

// Wrap JU to WJU
const depositTx = await wju.deposit({ value: ethers.utils.parseEther("1.0") });
await depositTx.wait();

// Or directly send JU to WJU contract address
const directTx = await signer.sendTransaction({
    to: WJU_ADDRESS,
    value: ethers.utils.parseEther("1.0")
});
await directTx.wait();

// Unwrap WJU to JU
const withdrawTx = await wju.withdraw(ethers.utils.parseEther("1.0"));
await withdrawTx.wait();

Query Balances

// Query WJU balance
const wjuBalance = await wju.balanceOf(userAddress);
console.log("WJU Balance:", ethers.utils.formatEther(wjuBalance));

// Query JU balance
const juBalance = await provider.getBalance(userAddress);
console.log("JU Balance:", ethers.utils.formatEther(juBalance));

Listen to Events

// Listen to wrapping events
wju.on("Deposit", (sender, amount, event) => {
    console.log(`${sender} wrapped ${ethers.utils.formatEther(amount)} JU`);
});

// Listen to unwrapping events
wju.on("Withdrawal", (sender, amount, event) => {
    console.log(`${sender} unwrapped ${ethers.utils.formatEther(amount)} WJU`);
});

Security Considerations

Contract Security

  1. Simple Design: WJU contract logic is simple, reducing security risks

  2. No Admin: Contract has no admin functions, completely decentralized

  3. 1:1 Exchange: Strict 1:1 exchange ratio that cannot be manipulated

Usage Security

  1. Balance Checks: All operations check if balance is sufficient

  2. Overflow Protection: Uses Solidity 0.8.21's built-in overflow protection

  3. Event Logging: All operations have corresponding event records

Best Practices

For Users

  1. Timely Unwrapping: If not needed for DEX use, promptly unwrap WJU to JU

  2. Balance Management: Maintain appropriate WJU balance for trading

  3. Gas Optimization: Batch operations to save gas fees

For Developers

  1. Path Validation: In JU-related trades, ensure path correctly includes WJU

  2. Balance Display: Show both JU and WJU balances in UI

  3. Auto Wrapping: Provide automatic wrapping/unwrapping functionality for users

Summary

WJU wrapped token is an important component of the JAMM DEX ecosystem:

  1. Seamless Integration: Enables JU tokens to participate in all DEX functions

  2. 1:1 Exchange: Guarantees complete value parity

  3. Automation: Router contract automatically handles wrapping/unwrapping

  4. Standard Compliance: Fully compliant with ERC-20 standard

  5. Decentralization: No need to trust third parties, completely managed by smart contracts

The existence of WJU enables JuChain users to conveniently use native tokens to participate in DeFi activities. In the next section, we will explore LP Token Mechanisms.