-
Notifications
You must be signed in to change notification settings - Fork 504
Description
Description
exchange.multi_sig() always returns {"status": "err", "response": "Invalid multi-sig outer signer"} when used with User-Signed Actions like sendAsset. The inner signatures are generated using sign_multi_sig_user_signed_action_payload(), and the outer signature is generated by sign_multi_sig_action() inside exchange.multi_sig().
Environment
- SDK version: 0.22.0 (also tested with 0.20.0)
- Python: 3.11 and 3.13
- Network: Mainnet (
https://api.hyperliquid.xyz)
Steps to Reproduce
- Create a 2/2 multi-sig account with two authorized users (A and B)
- Sign a
sendAssetinner action with both A and B usingsign_multi_sig_user_signed_action_payload() - Submit via
exchange.multi_sig()where the Exchange is initialized with wallet A (an authorized user) - Response:
{"status": "err", "response": "Invalid multi-sig outer signer"}
Code
```python
from eth_account import Account
from hyperliquid.exchange import Exchange
from hyperliquid.utils.signing import SEND_ASSET_SIGN_TYPES, sign_multi_sig_user_signed_action_payload
MULTISIG_USER = "0x5c99..." # multi-sig account
KEY_A = "0x..." # authorized user A
KEY_B = "0x..." # authorized user B
outer_wallet = Account.from_key(KEY_A)
outer_signer = outer_wallet.address.lower()
nonce = int(time.time() * 1000)
action = {
"type": "sendAsset", "nonce": nonce - 1, "token": "USDC", "amount": "5.0",
"sourceDex": "", "destination": "0x...", "destinationDex": "", "fromSubAccount": "",
}
sig1 = sign_multi_sig_user_signed_action_payload(
wallet=Account.from_key(KEY_A), action=action, is_mainnet=True,
sign_types=SEND_ASSET_SIGN_TYPES, tx_type="HyperliquidTransaction:SendAsset",
payload_multi_sig_user=MULTISIG_USER, outer_signer=outer_signer,
)
sig2 = sign_multi_sig_user_signed_action_payload(
wallet=Account.from_key(KEY_B), action=action, is_mainnet=True,
sign_types=SEND_ASSET_SIGN_TYPES, tx_type="HyperliquidTransaction:SendAsset",
payload_multi_sig_user=MULTISIG_USER, outer_signer=outer_signer,
)
action["hyperliquidChain"] = "Mainnet"
action["signatureChainId"] = "0x66eee"
exchange = Exchange(outer_wallet, "https://api.hyperliquid.xyz")
result = exchange.multi_sig(MULTISIG_USER, action, [sig1, sig2], nonce)
print(result) # {"status": "err", "response": "Invalid multi-sig outer signer"}
```
Key observations
- The same wallet (A) that is verified as an authorized user on-chain (confirmed via
userToMultiSigSignersAPI) producesInvalid multi-sig outer signer - Normal (non-multi-sig)
sendAssetwith the same wallet works fine - A freshly created multi-sig account with brand new wallets also fails the same way
- The TypeScript SDK (viem) with identical parameters succeeds, suggesting the issue is specific to the Python SDK's
sign_multi_sig_action()oraction_hash()implementation
Possibly related
- Issue Unable to convert from multisig account back to regular #252 (Unable to convert from multisig account back to regular)
- Issue Is signatureChainId intentionally hardcoded to 0x66eee in sign_user_signed_action? #254 (signatureChainId hardcoded to 0x66eee)
Could there be a msgpack serialization mismatch between the Python SDK and the Hyperliquid backend when computing the action hash for the outer SendMultiSig signature?