ERC20 semantics
XPower Banq's Position contracts implement ERC20, but the Borrow Position inverts the direction. This page is the precise specification.
Supply Position (standard ERC20)
Behaviour matches OpenZeppelin's reference ERC20, with the following additions:
- Index-aware balances.
balanceOf(account)returns principal × (current_index / account_index). Balance grows with interest accrual. - Proportional lock transfer.
transferandtransferFrommove the lock fraction proportionally. - Health check on sender. If the sender has any debt, post-transfer H must be ≥ 1.
Borrow Position (inverted ERC20)
Inverted direction. The semantics:
balanceOf(account)returns the account's debt — what they owe.transfer(from, amount)moves debt fromfromto the caller. The caller's debt increases. The source's debt decreases. The naming is unusual: think of it as "I'm pulling debt from you."transferFrom(from, to, amount)moves debt fromfromtoto. Both parties must approve.- Health check on receiver. Post-transfer, the debt receiver must have H ≥ 100%.
The protocol bypasses approvals when the caller is the Pool contract — this is what enables debt-assumption liquidation.
Why inverted?
Because debt has inverted utility. Pushing debt from sender to receiver harms the receiver. Standard ERC20 push semantics would let anyone dump debt onto unwilling counterparties. Pull semantics with two-sided approval makes debt transfers genuinely consensual.
Common operations
Read your supply
uint256 myBalance = supplyPosition.balanceOf(msg.sender);Transfer your supply
supplyPosition.transfer(recipient, amount); // standard ERC20 transferRead your debt
uint256 myDebt = borrowPosition.balanceOf(msg.sender);Take on someone else's debt (with approval)
// Source has previously called: borrowPosition.approve(msg.sender, amount);
borrowPosition.transfer(source, amount); // pull debt FROM source TO caller (msg.sender)Read aggregated state
uint256 supplyAccountIndex = supplyPosition.indexOf(account);
uint256 supplyGlobalIndex = supplyPosition.index();Decimals
Position tokens have decimals matching the underlying asset, with a minimum of 6. So a XPOW supply position has 6 decimals; a WAPOW supply position has 18.
Names and symbols
The standard name() and symbol() strings are derived from the underlying token's name() and symbol(), plus the "buddy" token of the pool pair:
- Supply name:
"<token.name()> Supply"— e.g. an XPOW supply position renders as"XPower XPOW Supply". - Supply symbol:
"s<TOKEN>:<BUDDY>"— e.g."sXPOW:USDC"for the XPOW side of an XPOW/USDC pool. - Borrow name:
"<token.name()> Borrow". - Borrow symbol:
"b<TOKEN>:<BUDDY>"— e.g."bUSDC:XPOW"for the USDC side of an XPOW/USDC pool.
Two special-cases apply (see the Position constructors):
- The native APOW/XPOW pair collapses to a single-token suffix:
sAPOW/sXPOW(notsAPOW:XPOW) and likewisebAPOW/bXPOW. WAVAXis renamed toAVAXin either side of the pair: e.g.sAVAX:APOWrather thansWAVAX:APOW.
Where to go next
- Positions as tokens — conceptual overview
- Architecture overview — the broader contract picture