Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- **Burned-token classification in `get_positions_by_owner` is provider-dependent no more.** `ownerOf` reverts for burned positions; some RPC providers wrap that revert as a raw JSON-RPC error response, which the scan classified as a transport failure and propagated — aborting the enumeration on any market with a burned position. Reverts are now recognized by their revert data regardless of how the provider encodes them; only genuine transport failures propagate.
- **ETH transfer gas on Arbitrum.** `transfer_eth` hardcoded `GasLimits::ETH_TRANSFER` (21,000), the Ethereum L1 intrinsic. Arbitrum folds an L1 data component into intrinsic gas, so the node rejected these transfers as "intrinsic gas too low", breaking cohort wallet funding. Transfers now estimate gas via `eth_estimateGas` (like other operations); `simulate()` handles the empty calldata of a plain transfer. `GasLimits::ETH_TRANSFER` remains as a reference value.
- **In-flight transaction eviction.** Failed `poll_receipt` and on-chain reverts now evict the transaction from the pipeline's in-flight map, preventing permanent slot consumption. Previously, 16 failed transactions would jam the pipeline and block all new transactions including closes and retreats.

Expand Down
17 changes: 13 additions & 4 deletions src/client/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,22 @@ impl PerpClient {
let mut owned = Vec::new();
for id in 1..total {
let pos_id = U256::from(id);
// ownerOf reverts for burned/non-existent tokens — those
// surface as contract errors, which we skip. Other transport
// errors propagate so network failures aren't silently ignored.
// ownerOf reverts for burned/non-existent tokens — skip those.
// How a revert surfaces is provider-dependent: some decode into
// contract errors, others wrap the raw JSON-RPC error response
// (code 3, revert data attached) as a transport error. Classify
// by revert data, and only propagate genuine transport failures
// so network errors aren't silently ignored.
match perp.ownerOf(pos_id).call().await {
Ok(addr) if addr == owner => owned.push(pos_id),
Ok(_) => {}
Err(e @ alloy::contract::Error::TransportError(_)) => return Err(e.into()),
Err(alloy::contract::Error::TransportError(e))
if e.as_error_resp()
.and_then(|resp| resp.as_revert_data())
.is_none() =>
{
return Err(alloy::contract::Error::TransportError(e).into());
}
Err(_) => {} // burned or non-existent token
}
}
Expand Down
Loading