Deposits & Account Creation
This guide covers how to deposit collateral into your Synthetix trading account and how accounts are created.
Overview
Deposits are on-chain transactions made directly to the SynthetixDepositContract smart contract. Your first deposit automatically creates your trading account.
Contract Addresses
| Network | Contract | Address |
|---|---|---|
| Mainnet | SynthetixDepositContract (Proxy) | 0xD62595c3c23B690BAEE0935e107A209Cb1Dbd37B |
| Mainnet | USDT | 0xdAC17F958D2ee523a2206206994597C13D831ec7 |
| Mainnet | Permit2 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| Sepolia | SynthetixDepositContract (Proxy) | 0x5Ed4a299E9fa36E6bDb4E0723bD3ad9D233f33A0 |
| Sepolia | USDT (Mock) | 0xE02d1640dcaB494327DbE4B63fBcD441b92c8205 |
Supported Collateral
Currently supported collateral types:
| Asset | Decimals | Network |
|---|---|---|
| USDT | 6 | Mainnet |
Each collateral has configurable limits:
- Minimum Deposit: Minimum amount per deposit transaction
- Maximum Balance: Maximum total balance per user
- Minimum Withdrawal: Minimum amount per withdrawal
Deposit Flow
Account Creation
Your first deposit automatically creates a master account. No separate account creation step is required.
Understanding Account IDs:subAccountId: The unique identifier for any account (master or sub). This is a non-zero value (e.g.,987654321) generated when the account is created.masterId: Indicates the account hierarchy:masterId = 0means this account IS the master accountmasterId = <non-zero ID>means this is a subaccount belonging to that master
When depositing, passing subAccountId: 0 in the request is a special value that means "deposit to my master account" - it does not mean the master account has ID 0.
Standard Deposit (ERC20 Approve + Deposit)
Deposits require two transactions:
- Approve: Allow the deposit contract to spend your tokens
- Deposit: Call the deposit function on the contract
JavaScript Example (ethers.js v6)
import { ethers } from 'ethers';
// Contract addresses (Mainnet)
const DEPOSIT_CONTRACT = '0xD62595c3c23B690BAEE0935e107A209Cb1Dbd37B';
const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7';
// ABI for deposit function
const depositABI = [
'function deposit((address token, uint256 amount, address beneficiary, uint256 subAccountId, (((address token, uint256 amount) permitted, uint256 nonce, uint256 deadline) permit, bytes signature) permitDetails)[] deposits)'
];
const erc20ABI = [
'function approve(address spender, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)'
];
async function deposit(signer, amount) {
const walletAddress = await signer.getAddress();
// Step 1: Approve USDT spending (if needed)
const usdt = new ethers.Contract(USDT, erc20ABI, signer);
const currentAllowance = await usdt.allowance(walletAddress, DEPOSIT_CONTRACT);
if (currentAllowance < amount) {
console.log('Approving USDT...');
const approveTx = await usdt.approve(DEPOSIT_CONTRACT, amount);
await approveTx.wait();
console.log('Approval confirmed');
}
// Step 2: Deposit
const depositContract = new ethers.Contract(DEPOSIT_CONTRACT, depositABI, signer);
const tx = await depositContract.deposit([{
token: USDT,
amount: amount,
beneficiary: walletAddress,
subAccountId: 0, // 0 = deposit to master account (auto-created on first deposit)
permitDetails: {
permit: {
permitted: { token: ethers.ZeroAddress, amount: 0 },
nonce: 0,
deadline: 0
},
signature: '0x'
}
}]);
console.log('Deposit transaction sent:', tx.hash);
const receipt = await tx.wait();
console.log('Deposit confirmed in block:', receipt.blockNumber);
return receipt;
}
// Usage
const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY');
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const amount = ethers.parseUnits('1000', 6); // 1000 USDT
await deposit(signer, amount);Python Example (web3.py)
from web3 import Web3
import os
# Contract addresses (Mainnet)
DEPOSIT_CONTRACT = '0xD62595c3c23B690BAEE0935e107A209Cb1Dbd37B'
USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7'
# Connect to Ethereum
w3 = Web3(Web3.HTTPProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'))
account = w3.eth.account.from_key(os.environ['PRIVATE_KEY'])
# ERC20 ABI (minimal)
erc20_abi = [
{
"name": "approve",
"type": "function",
"inputs": [
{"name": "spender", "type": "address"},
{"name": "amount", "type": "uint256"}
],
"outputs": [{"type": "bool"}]
}
]
# Deposit contract ABI (minimal)
deposit_abi = [
{
"name": "deposit",
"type": "function",
"inputs": [{
"name": "deposits",
"type": "tuple[]",
"components": [
{"name": "token", "type": "address"},
{"name": "amount", "type": "uint256"},
{"name": "beneficiary", "type": "address"},
{"name": "subAccountId", "type": "uint256"},
{"name": "permitDetails", "type": "tuple", "components": [
{"name": "permit", "type": "tuple", "components": [
{"name": "permitted", "type": "tuple", "components": [
{"name": "token", "type": "address"},
{"name": "amount", "type": "uint256"}
]},
{"name": "nonce", "type": "uint256"},
{"name": "deadline", "type": "uint256"}
]},
{"name": "signature", "type": "bytes"}
]}
]
}]
}
]
def deposit(amount_usdt):
amount = amount_usdt * 10**6 # USDT has 6 decimals
# Step 1: Approve
usdt = w3.eth.contract(address=USDT, abi=erc20_abi)
approve_tx = usdt.functions.approve(DEPOSIT_CONTRACT, amount).build_transaction({
'from': account.address,
'nonce': w3.eth.get_transaction_count(account.address),
'gas': 100000,
'gasPrice': w3.eth.gas_price
})
signed_approve = account.sign_transaction(approve_tx)
tx_hash = w3.eth.send_raw_transaction(signed_approve.raw_transaction)
w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Approval confirmed: {tx_hash.hex()}")
# Step 2: Deposit
deposit_contract = w3.eth.contract(address=DEPOSIT_CONTRACT, abi=deposit_abi)
deposit_tx = deposit_contract.functions.deposit([{
'token': USDT,
'amount': amount,
'beneficiary': account.address,
'subAccountId': 0, # 0 = deposit to master account (auto-created on first deposit)
'permitDetails': {
'permit': {
'permitted': {'token': '0x0000000000000000000000000000000000000000', 'amount': 0},
'nonce': 0,
'deadline': 0
},
'signature': b''
}
}]).build_transaction({
'from': account.address,
'nonce': w3.eth.get_transaction_count(account.address),
'gas': 200000,
'gasPrice': w3.eth.gas_price
})
signed_deposit = account.sign_transaction(deposit_tx)
tx_hash = w3.eth.send_raw_transaction(signed_deposit.raw_transaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Deposit confirmed in block: {receipt['blockNumber']}")
return receipt
# Usage
deposit(1000) # Deposit 1000 USDTDepositing to a Specific Subaccount
Once you have created additional subaccounts under your master account, you can deposit directly to them by specifying their subAccountId:
// Deposit to a specific subaccount
const tx = await depositContract.deposit([{
token: USDT,
amount: amount,
beneficiary: walletAddress,
subAccountId: 987654321, // Specific subaccount ID
permitDetails: {
permit: {
permitted: { token: ethers.ZeroAddress, amount: 0 },
nonce: 0,
deadline: 0
},
signature: '0x'
}
}]);Gasless Deposits (Permit2)
For a better user experience, you can use Permit2 to combine approval and deposit into a single signature + transaction:
- Sign a Permit2 message authorizing the transfer
- Call
deposit()with the permit details
This avoids the separate approval transaction. The Permit2 contract address is 0x000000000022D473030F116dDEE9F6B43aC78BA3 (same on all networks).
Verifying Your Deposit
After submitting a deposit transaction, you can verify it was successful in two ways:
1. Check Transaction Receipt
The deposit emits an AssetDeposited event:
// Check for AssetDeposited event in receipt
const AssetDepositedTopic = ethers.id(
"AssetDeposited(address,address,address,uint256,uint256)"
);
const depositEvent = receipt.logs.find(
log => log.topics[0] === AssetDepositedTopic
);
if (depositEvent) {
console.log('Deposit successful!');
// Parse event data: depositor, beneficiary, token, amount, subAccountId
}2. Query via API
After a short indexing delay (typically 5-30 seconds), your deposit will appear in the API:
import axios from 'axios';
async function verifyDeposit(subAccountId, signature, expiresAfter) {
const response = await axios.post('https://papi.synthetix.io/v1/trade', {
params: {
action: 'getBalanceUpdates',
subAccountId: subAccountId,
actionFilter: 'DEPOSIT',
limit: 5
},
signature: signature,
expiresAfter: expiresAfter
});
return response.data.response.balanceUpdates;
}See Get Balance Updates for full API documentation.
Verification Flow
- Submit transaction: Call
approve()thendeposit()on the contract - Get receipt: Transaction receipt contains
AssetDepositedevent - Wait for indexing: Events are indexed within ~5-30 seconds
- Query API: Call
getBalanceUpdatesto confirm deposit appears in history
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Transaction reverts | Insufficient approval | Ensure you've approved enough tokens for the deposit contract |
| "Amount below minimum" | Deposit too small | Check minimum deposit requirements |
| "Exceeds maximum" | User or global cap reached | Contact team about limits |
| Deposit not showing in API | Indexing delay | Wait 30-60 seconds and retry |
Related
- Get Balance Updates - View deposit and withdrawal history
- Get Subaccount - Check current balances and margin status
- Withdraw Collateral - Withdraw funds via API
- Environments - API endpoints and configuration