Skip to content

Submit Single Join

POST /intents/submit (type: "join")

Submit a signed single-asset join intent. The user deposits one token into a pool and receives LP shares. Swap fees apply since the pool needs to rebalance internally.

Full Example (viem)

javascript
import { parseUnits } from 'viem';

const MULTIHOP_ROUTER = '0x662F15226f5b6Bf8aA10512374Af3115412C04bA';
const RELAYER_URL = 'https://mainnet.relayer.ryze.pro/api/v1';

// 1. Get a join quote from the Router
const quoteRes = await fetch('https://mainnet.router.ryze.pro/join-quote', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    poolAddress: '0x7B41aA91947398CD9244AD4e314C253D9B1B5206',
    tokensIn: [{
      token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
      amount: parseUnits('100', 6).toString(),
    }],
  }),
});
const quote = await quoteRes.json();

// 2. Read nonce
const nonce = await publicClient.readContract({
  address: MULTIHOP_ROUTER,
  abi: [{ name: 'joinNonces', type: 'function', inputs: [{ type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' }],
  functionName: 'joinNonces',
  args: [account.address],
});

// 3. Build intent
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1800);
const minSharesOut = BigInt(quote.sharesOut) * 995n / 1000n; // 0.5% slippage

const intent = {
  user: account.address,
  pool: '0x7B41aA91947398CD9244AD4e314C253D9B1B5206',
  tokenIn: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  amountIn: parseUnits('100', 6),
  minSharesOut,
  recipient: account.address,
  deadline,
  nonce,
};

// 4. Sign EIP-712
const signature = await walletClient.signTypedData({
  domain: {
    name: 'MultiHopRouter',
    version: '1',
    chainId: 8453,
    verifyingContract: MULTIHOP_ROUTER,
  },
  types: {
    JoinIntent: [
      { name: 'user', type: 'address' },
      { name: 'pool', type: 'address' },
      { name: 'tokenIn', type: 'address' },
      { name: 'amountIn', type: 'uint256' },
      { name: 'minSharesOut', type: 'uint256' },
      { name: 'recipient', type: 'address' },
      { name: 'deadline', type: 'uint256' },
      { name: 'nonce', type: 'uint256' },
    ],
  },
  primaryType: 'JoinIntent',
  message: intent,
});

// 5. Split signature and submit
const r = signature.slice(0, 66);
const s = '0x' + signature.slice(66, 130);
const v = parseInt(signature.slice(130, 132), 16);

const res = await fetch(`${RELAYER_URL}/intents/submit`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    type: 'join',
    user: intent.user,
    pool: intent.pool,
    tokenIn: intent.tokenIn,
    amountIn: intent.amountIn.toString(),
    minSharesOut: intent.minSharesOut.toString(),
    recipient: intent.recipient,
    deadline: Number(intent.deadline),
    nonce: Number(intent.nonce),
    signature: { v, r, s },
  }),
});
const result = await res.json();
console.log('Intent ID:', result.intentId);

Request Body

json
{
  "type": "join",
  "user": "0xYourAddress",
  "pool": "0x7B41aA91947398CD9244AD4e314C253D9B1B5206",
  "tokenIn": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "amountIn": "100000000",
  "minSharesOut": "99002500000000000000",
  "recipient": "0xYourAddress",
  "deadline": 1713200000,
  "nonce": 0,
  "signature": { "v": 27, "r": "0x...", "s": "0x..." }
}

Fields

FieldTypeDescription
typestringMust be "join"
useraddressSender (must match signature)
pooladdressPool to join
tokenInaddressToken being deposited
amountInstringDeposit amount (raw units)
minSharesOutstringMinimum LP shares to receive
recipientaddressAddress to receive LP shares
deadlinenumberUnix timestamp expiry
noncenumberFrom joinNonces(user) on contract
signatureobjectEIP-712 signature {v, r, s}

EIP-712 Types

typescript
const types = {
  JoinIntent: [
    { name: 'user', type: 'address' },
    { name: 'pool', type: 'address' },
    { name: 'tokenIn', type: 'address' },
    { name: 'amountIn', type: 'uint256' },
    { name: 'minSharesOut', type: 'uint256' },
    { name: 'recipient', type: 'address' },
    { name: 'deadline', type: 'uint256' },
    { name: 'nonce', type: 'uint256' },
  ],
};

Response (202 Accepted)

json
{
  "success": true,
  "intentId": "0x...",
  "status": "pending",
  "message": "Intent submitted successfully"
}