Smart Contract Reference
Two contracts make up Partner Staking: PartnerStakedXDCV32 (the per-partner vault; legacy pools run PartnerStakedXDCV3) and PartnerVaultRegistry (the shared directory).
Two contracts make up Partner Staking: PartnerStakedXDC_V3_2 (the per-partner vault; legacy pools run PartnerStakedXDC_V3) and PartnerVaultRegistry (the shared directory).
Live on XDC Mainnet. The shared PartnerVaultRegistry is at 0x325DEEA5C7c0Ce0D774c4A67EcCaAf1cF8953a67. Each partner vault is a separate per-partner deployment; discover live pools via the registry's allVaults() / verifiedVaults(). See Deployed Contracts & Addresses.
PartnerStakedXDC_V3_2 — the partner vault
An ERC-4626, native-XDC, share-based liquid staking vault (ReentrancyGuard, ERC4626, Pausable, AccessControl). It is a fee-bearing copy of the flagship PrimeStakedXDC_V3_1 with separate state, token, keys, and operators. asset() is the zero address because the underlying is native XDC; totalAssets() returns trackedTotalAssets. V3.2 adds the timelocked partner fee (see below); everything else matches the audited V3 template.
Constants
| Constant | Value | Meaning |
|---|---|---|
PLATFORM_FEE_BPS | 1500 | 15% protocol fee on reward inflows |
PLATFORM_FEE_RECIPIENT | 0x1658…9127 | StakeXDC treasury (fee recipient) |
MAX_PARTNER_FEE_BPS | 8500 | Partner fee ceiling: protocol + partner can never exceed 100% of yield |
PARTNER_FEE_PUSH_GAS_LIMIT | 100,000 | Gas stipend for the partner fee push (failure defers to the pull lane) |
DEFAULT_MASTERNODE_STAKE | 10,000,000 XDC | Default masternode size |
MIN_REWARD_DISTRIBUTION | 1000 XDC | Minimum non-privileged reward push via receive() |
MAX_BUFFER_BPS | 5000 | Max liquidity buffer (50%) |
DEFAULT_MAX_LOSS_BPS_PER_REPORT | 1000 | 10% per-report validator-loss cap |
DEFAULT_MAX_DAILY_LOSS_BPS | 2000 | 20% rolling daily loss cap |
DEFAULT_GOVERNANCE_DELAY | 1 day | Default timelock (range 1 min – 30 days) |
Partner fee (V3.2)
| Item | Detail |
|---|---|
partnerFeeBps / partnerFeeRecipient | Current fee (bps of gross yield) and payee. Set in the constructor (initialPartnerFeeBps, initialPartnerFeeRecipient; zero recipient defaults to the deployer). |
setPartnerFee(bps) → executePartnerFee() / cancelPartnerFeeChange() | Timelocked rate change (admin only, waits governanceDelay). Executing first settles accrued yield at the old rate. |
setPartnerFeeRecipient(addr) → executePartnerFeeRecipient() / cancelPartnerFeeRecipientChange() | Timelocked recipient change (admin only). |
pendingPartnerFee() / pendingPartnerFeeRecipient() | Views exposing any scheduled change + its executeAfter timestamp (drives the app's staker warnings). |
| Failure mode | If the recipient rejects the push, the fee parks in pendingQueuedAssets[recipient] and is pulled via claimQueuedAssets — the pool never bricks. |
Stake & withdraw
| Function | Notes |
|---|---|
stake(uint256 assets) payable | Native-XDC stake; msg.value must equal assets. Mints shares at the current rate. |
depositNative(uint256 assets, address receiver) payable | Same, crediting receiver. |
deposit(...) / mint(...) | Disabled. Revert NativeDepositRequired (deposits must be native, via the payable wrappers). |
withdraw(uint256 shares) | Legacy-compatible redeem-by-shares. |
withdraw(uint256 assets, address receiver, address owner) / redeem(...) | Standard ERC-4626 exits; revert if the liquid buffer can't cover them. |
withdrawWithQueue(uint256 assets, address receiver) | Instant if liquid, else queues a FIFO request. |
redeemWithQueue(uint256 shares, address receiver) | Same, by shares. |
cancelQueuedWithdrawal(uint256 requestId) | Cancel an unprocessed queued request; shares returned. |
processWithdrawalQueue(uint256 maxRequests) | Anyone can advance the queue once liquidity returns. |
claimQueuedAssets(address payable receiver) | Claim a deferred payout (if a queued transfer failed). |
maxWithdraw / maxRedeem | Clamped to what the buffer can pay immediately. |
Masternodes & operators
| Function | Role |
|---|---|
addOperator / removeOperator | DEFAULT_ADMIN_ROLE |
submitKYC(string) | DEFAULT_ADMIN_ROLE |
proposeMasternode(address candidate, uint256 amount) | proposer or admin |
triggerAutoPropose(uint256 maxNodes) | anyone (round-robins operators when funded) |
resignMasternode(address) / withdrawResignedMasternode(uint256) | proposer or admin |
setValidator / setMinStake / setBufferBps / setAutoProposeConfig / setOperatorScanLimit / setQueueScanLimit | OPERATIONS_MANAGER_ROLE |
reportValidatorLoss(address operator, uint256 assets) | RISK_MANAGER_ROLE, within per-report & daily caps |
Governance (timelocked, two-phase)
Direct grantRole / revokeRole / renounceRole / transferOwnership / renounceOwnership all revert. Use the scheduled flow instead:
| Schedule | Execute | Cancel |
|---|---|---|
setProposer | executeProposer | cancelProposerChange |
setOperationsManager | executeOperationsManager | cancelOperationsManagerChange |
setRiskManager | executeRiskManager | cancelRiskManagerChange |
setMaxLossBpsPerReport | executeMaxLossBpsPerReport | cancelMaxLossBpsPerReportChange |
setMaxDailyLossBps | executeMaxDailyLossBps | cancelMaxDailyLossBpsChange |
setGovernanceDelay | executeGovernanceDelay | cancelGovernanceDelayChange |
setPartnerFee | executePartnerFee | cancelPartnerFeeChange |
setPartnerFeeRecipient | executePartnerFeeRecipient | cancelPartnerFeeRecipientChange |
scheduleOwnerTransfer | executeOwnerTransfer | cancelOwnerTransfer |
Each executes only after governanceDelay has elapsed.
Key views & marker
| Function | Returns |
|---|---|
isPartnerStakedXDCV3() | true — marker the registry/UI use to identify partner vaults (kept in V3.2) |
partnerVaultVersion() | 2 on V3.2 pools; the call reverts on legacy V3 pools, which is how the app tells generations apart |
name() / symbol() | The partner's branded name/symbol |
totalAssets() / desiredBuffer() | Tracked NAV and target buffer |
isKYCVerified(address) | Whether an address is KYC'd on the validator |
Notable events
Staked, Withdrawn, WithdrawalQueued / WithdrawalQueueProcessed / WithdrawalQueueCancelled, MasternodeProposed, ValidatorLossReported, PlatformFeeSkimmed(recipient, amount) (every protocol fee skim), and on V3.2: PartnerFeeSkimmed / PartnerFeeDeferred (partner fee payouts) plus PartnerFeeChangeScheduled / PartnerFeeUpdated / PartnerFeeChangeCancelled and the recipient-change equivalents (timelock transparency).
PartnerVaultRegistry: the directory
Ownable2Step; owner is StakeXDC. See Registry & Verification for the full write-up.
| Function | Caller | Purpose |
|---|---|---|
setCanonicalCodeHash(bytes32, bool) | owner | Allow-list / revoke a canonical vault bytecode hash |
register(address vault) | vault admin | List a vault (requires canonical codehash + admin role) |
setVerified(address, bool) | owner | Toggle the "Verified by StakeXDC" badge |
unregister(address) | owner | Delist an abusive/abandoned pool |
setMetadata(address, PoolMeta) | vault admin | Set description / website / logo / socials |
allVaults / verifiedVaults / vaultsByAdmin / vaultAt / vaultsLength | view | Enumerate the directory |
isRegistered / isVerified / registrantOf / metadata | view | Per-vault status & data |
Events: CanonicalCodeHashSet, VaultRegistered, VaultVerifiedSet, VaultUnregistered, MetadataUpdated.
→ Partner Staking overview → How It Works → Deploy & List a Pool
Registry & Verification
PartnerVaultRegistry is the on-chain directory that powers the StakeXDC app's partner pool listing. It answers one question trust-minimally: 'is this address a genuine, unmodifi…
Deployed contracts and addresses
Canonical address book for StakeXDC V3.2, covering the psXDC vault, airdrop distributor, referral program, XDC spot venue, and legacy contracts.