Exchange endpoint: createSubAccount, subAccountTransfer - #153
Merged
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #153 +/- ##
==========================================
+ Coverage 69.84% 70.02% +0.17%
==========================================
Files 53 53
Lines 8517 8640 +123
Branches 531 533 +2
==========================================
+ Hits 5949 6050 +101
- Misses 2565 2587 +22
Partials 3 3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Adds the two missing /exchange actions for sub-account management (issue #121): createSubAccount (returns the new sub-account's address) and subAccountTransfer (deposit/withdraw USDC into/from a sub-account). Both are L1 actions signed with the master/agent key directly, and neither takes a vaultAddress parameter since they either have no vault-like target (createSubAccount) or already name one via subAccountUser (subAccountTransfer) - same rule already applied to vaultTransfer/hip3LiquidatorTransfer. Field shapes were cross-checked against the official hyperliquid-python-sdk (create_sub_account/sub_account_transfer) and the nktkas/hyperliquid TS SDK's CreateSubAccountRequest/ SubAccountTransferRequest schemas, plus the pre-existing CreateSubAccountAction/SubAccountTransferAction reference signature vectors in signing_test.cpp. Wires up RestApi::createSubAccount/subAccountTransfer (sync + async), WebsocketApi::createSubAccount/subAccountTransfer, request-builder and response-parser tests in transfers_test.cpp, a wallet-required smoke test in websocket_api_exchange_test.cpp, testnet examples, and marks both rows done in the README coverage table. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Le73N2EZjGLsCqmCScAse
TuxedoFish
force-pushed
the
endpoint/create-subaccount-transfer
branch
from
September 8, 2026 12:48
e810a09 to
1122ab2
Compare
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #121.
Adds the two missing
/exchangeactions that complete sub-account support (the SDK already has the read-sidesubAccountsendpoint, but no way to create a sub-account or move funds into/out of one):createSubAccount-RestApi::createSubAccount/WebsocketApi::createSubAccount. Returns aCreateSubAccountResponsewith the new sub-account's address insubAccountUser.subAccountTransfer-RestApi::subAccountTransfer/WebsocketApi::subAccountTransfer. Deposit/withdraw USDC into/from a sub-account (dollars in the request struct, converted to raw 1e6 USDC units in the builder, same asvaultTransfer).Both are plain L1 actions (signed with the master/agent key directly, not EIP-712 user-signed), and neither takes a
vaultAddressparameter -createSubAccounthas no vault-like target at all, andsubAccountTransferalready names its target viasubAccountUser, same reasoning already applied tovaultTransfer/hip3LiquidatorTransferin this codebase. Also excluded both fromApiConfig::vaultAddressfallback inSigning::prepareBodyForTypefor the same reason.Field-shape verification - now run against live testnet
Ran both actions live against testnet using a real signed request from this repo's test wallet:
createSubAccount- real testnet request/responseThis confirms the request shape is correct - the server parsed and understood it, then rejected it on a legitimate business rule (this wallet's trading volume), not a schema/deserialization error. It also confirms the error-path response parsing works:
CreateSubAccountResponse::status/errorwere populated correctly from a real error payload. The success-path response shape (response.data= the new address) still couldn't be confirmed live, since no wallet with $100k+ traded volume was available to actually create one - it remains cross-checked against the TS SDK only, as before. Flagging this rather than overstating full coverage.subAccountTransfer- real testnet attempt, inconclusiveAttempted a transfer to a placeholder address (no real sub-account existed to target, since
createSubAccountabove didn't succeed):This is not evidence of a bug in our request shape - independently re-confirmed
usd's type against the TS SDK source (UnsignedInteger, "amount * 1e6"), which matches what we send exactly. The far more likely explanation is that0x000...aisn't a real, registered sub-account of this wallet (it's a placeholder, since no sub-account could be created to test against), and the server's address-ownership validation appears to fail before reaching its normal JSON-wrapped error response - hence the raw-text body instead of the usual{"status":"err",...}shape. Our client-side handling of this degraded gracefully (caught, logged, returned asstatus="err"), which is the existing sharedSimpleResponseparsing behavior, not something new to this PR.Net effect:
subAccountTransfercould not be end-to-end confirmed against a real successful transfer, for the same underlying reason ascreateSubAccount's success path - this test wallet can't create/access a real sub-account without $100k of trading volume. The request shape is independently confirmed correct against the TS SDK; the full round-trip (a real deposit into a real sub-account) is not.Changes
include/hyperliquid/types/RequestTypes.h:RestEndpointType::CreateSubAccount/SubAccountTransfer(+toString/isAuthenticated),CreateSubAccountRequest/SubAccountTransferRequeststructs.include/hyperliquid/types/ResponseTypes.h:CreateSubAccountResponse(subAccountTransfer reuses the existing genericSimpleResponse).src/messages/ExchangeRequestBuilder.h/.cpp: builders for both actions.include/hyperliquid/rest/RestApiMessageParser.h/.cpp:parseCreateSubAccount.include/hyperliquid/rest/RestEndpointListener.h:onCreateSubAccountcallback.include/hyperliquid/rest/RestApi.h/src/rest/RestApi.cpp: sync + async methods.include/hyperliquid/websocket/WebsocketApi.h/src/websocket/WebsocketApi.cpp: websocket wrappers via the genericsignAndSend.src/signing/Signing.cpp: excluded both action types from theApiConfig::vaultAddressfallback.tests/transfers_test.cpp: request-builder tests (body shape, usd scaling, reference-signature match) and response-parser tests for both actions.tests/websocket_api_exchange_test.cpp: wallet-required smoke test.examples/rest_sub_account.cpp,examples/ws_sub_account.cpp: new testnet examples (create a sub-account, then deposit into it).README.md: marked both rows done in the exchange-actions coverage table.Test plan
cmake -S . -B build -DHYPERLIQUID_WARNINGS_AS_ERRORS=ON -DHYPERLIQUID_BUILD_TESTS=ON -DHYPERLIQUID_BUILD_EXAMPLES=ON -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake"- clean configurecmake --build build -j "$(nproc)"- zero warnings/errorsctest --test-dir build- 100% (28/28 test suites) passingmain(post-Info endpoints: userBorrowLendInterest, liquidatable #154/Info endpoints: twapHistory, userTwapSliceFillsByTime, activeAssetData #157) - clean rebase, no conflicts, full rebuild/retest after rebasingcreateSubAccount's request/error-path fully confirmed; both actions' success paths remain unconfirmed since this wallet can't clear the $100k trading-volume gate to create a real sub-account to test against🤖 Generated with Claude Code
https://claude.ai/code/session_018Le73N2EZjGLsCqmCScAse