Withdraw
Take funds out of Native Core Pool — the two withdrawal types, the signed create, claim and cancel actions, and the status lifecycle.
A withdrawal moves funds from earn_balance back to the address's Native Core balance. Every step is a signed request; nothing is submitted on-chain by you.
To land the funds in an EVM wallet instead, see Withdraw Directly to EVM, which combines this step and the Native Core withdrawal into one request.
Choose a type at creation. The choice is permanent.
scheduled
instant
Fee
None
instant_fee_bps on the gross
Wait
withdraw_pending_seconds
None
Claim step
Required
None
Cancellable
During the wait only
Never
Ends at
claimed
completed
One address can have one withdrawal in flight per asset. The next one for that asset cannot be created until the current one reaches a terminal state; other assets are unaffected. The user_nonce is the one thing that is not per asset — a single counter per address covers all of them.
Read Native Core Pool first for the base URL, the response envelope, and discovery.
1. Check the account
curl -sS "$POOL_API_URL/api/v3/earn" \
-H 'content-type: application/json' \
-d '{"type":"account","user_address":"0x5555…5555"}'{
"code": 0,
"data": {
"user_address": "0x5555…5555",
"next_user_nonce": 3,
"balances": [
{
"asset_id": 2,
"earn_balance": "4000000000",
"in_queue_balance": "0",
"withdraw_locked_balance": "0",
"lifetime_deposit": "5000000000",
"lifetime_yield": "200000000",
"lifetime_withdraw": "1200000000",
"active_withdraw_operation_id": "",
"active_withdrawal": null
}
]
},
"message": "success"
}next_user_nonceis required to create a withdrawal. Read it fresh each time.active_withdraw_operation_idis the gate, and it is per asset. It lives on eachbalances[]entry, not at the top level. Find the entry whoseasset_idyou are withdrawing and read it there; an empty string means nothing is in flight for that asset. Another asset can be busy and this one still free.Use the id rather than
active_withdrawal, which is a convenience view that can benulleven while a withdrawal is active. Creating against a stale reading costs the user a signature and returnsuser asset already has an active withdrawal.next_user_noncestays at the top level because it is one counter per address, shared by every asset. Opening a USDT withdrawal also raises the nonce a later USDC withdrawal must beat.
2. Size the amount
You sign and submit the gross amount. The fee comes out of it.
A user asking to withdraw 100 receives 99.95 at 5 bps. Deriving gross from a target payout is your side of the calculation.
min_withdraw_amount and max_single_withdraw_amount from config are checked against the gross amount too. Both use "0" to mean no limit, and only positive values are enforced.
Amounts are 8-decimal atom strings, digits only. A zero-padded or signed value is rejected before it reaches the signature check.
3. Sign and submit
The domain is the eip712_domain object from config, passed through unchanged.
Three values are encoded differently in the request body and in the signature. Derive both from one object, because a mismatch reports only withdrawal signer does not match user, never which field is wrong.
withdraw_type
"scheduled" / "instant"
"1" / "2" as uint8
amount
Decimal atom string
Same value as uint256
deadline
Milliseconds
The same milliseconds
user_signature is the wallet's raw 65-byte signature, 0x plus 130 hex characters.
deadline_unix_ms must be in the future when the request arrives. A request carrying a past deadline returns deadline_unix_ms must be in the future. Set it five minutes ahead.
A successful response returns the created withdrawal record, in the shape described below.
4. Follow the status
operation_id is withdraw:<user_address>:<user_nonce>, and it is the handle for claim and cancel.
Each type walks its own path:
created
An instant withdrawal has been accepted
queued
A scheduled withdrawal is waiting for its claim window
authorizing
Authorization in progress
authorized
Authorized, payout not yet submitted
transferring
The payout transfer has been submitted
claimed
Terminal. A scheduled withdrawal was claimed and paid
completed
Terminal. An instant withdrawal was paid
cancelled
Terminal. A scheduled withdrawal was cancelled
manual_review
The payout failed and is held for manual intervention
These nine are also the accepted values for the status filter; any other value is rejected.
claimed_at_unix_ms, cancelled_at_unix_ms and completed_at_unix_ms are always present, and null until they happen. They are not mutually exclusive: claiming a scheduled withdrawal sets claimed_at and completed_at to the same value.
Two payout transaction hashes appear once the transfers are submitted, and are absent as keys until then:
core_tx_hash
The transfer to the user
Every withdrawal
fee_core_tx_hash
The transfer to the fee wallet
Instant withdrawals with a non-zero fee
5. Claim a scheduled withdrawal
Only scheduled withdrawals need this step, and only inside their window:
Read claimable_at_unix_ms; do not recompute it. The Pool sets it from server time when the withdrawal is created, so created_at_unix_ms + withdraw_pending_seconds does not reproduce it and fails at the boundary.
The signature is the same domain with a different primary type. operationId is signed as the keccak256 hash of the trimmed string, typed bytes32, while the request body carries the plain string. This is the most common mistake on this endpoint.
6. Cancel a scheduled withdrawal
A scheduled withdrawal can be cancelled only while all of the following hold:
At most one of claim and cancel is available at any moment. When withdraw_pending_seconds is 0 the withdrawal is claimable immediately and can never be cancelled.
The request is identical to claim with primaryType changed to CancelWithdrawal:
The status becomes cancelled and the gross amount returns from in_queue_balance to earn_balance, where it earns again.
When withdrawals are paused
withdrawal_paused in config blocks creating and claiming. Cancelling still works within its normal window, so a scheduled withdrawal that has not yet become claimable can still be reversed during a pause.
During a pause, an instant withdrawal and any scheduled withdrawal past its cancel window stay in their current status until the pause lifts.
What can go wrong
user asset already has an active withdrawal
One is already in flight for this address and asset
Check active_withdraw_operation_id on that asset's balances[] entry before signing
withdrawal signer does not match user
The typed data does not match, so a different address was recovered
Check the uint8 type code, the bytes32 operationId, the millisecond deadline, and that no chainId reached the domain
invalid signature recovery id
The 65-byte signature's v byte is not 0, 1, 27 or 28
Post the wallet's raw r‖s‖v; do not reorder or re-encode it
deadline_unix_ms must be in the future
The deadline has passed, including on a delayed claim retry
For a create, sign a new one; for a claim, poll withdrawals first
withdrawal is not claimable
Before claimable_at_unix_ms, or the status is no longer queued
Compare against claimable_at_unix_ms
withdrawal is not cancelable
Past claimable_at_unix_ms, or already claimed
Cancelling is no longer possible; claim instead
insufficient earn balance
The gross amount exceeds earn_balance
A withdrawal already in flight is not in earn_balance
withdrawal amount is below minimum
Gross below min_withdraw_amount
Re-read config; "0" means no minimum
withdrawal amount exceeds maximum
Gross above max_single_withdraw_amount
Re-read config; "0" means no maximum
withdrawals are paused
withdrawal_paused is true
Cancelling is still available in its window
withdrawal type is disabled for asset
The asset has that type turned off
Check scheduled_withdraw_enabled and instant_withdraw_enabled
withdrawal not found
No such record, or it belongs to another address
Both cases return the same response
Next steps
ReferenceYieldLast updated