Skip to content

Commit 78bd6ca

Browse files
committed
test: unchecked SPL transfer/approve require AdvancedMode; native ETH deposit uses address(0)
- Solana token transfer/approve/with_metadata now assert the unchecked variants are blocked without AdvancedMode and blind-sign with it (no signed mint to clear-sign against). - THORChain deposit fixture uses address(0) for native ETH — the only native form the pinned routers accept (0xEeee sentinel would revert on-chain).
1 parent 7231374 commit 78bd6ca

2 files changed

Lines changed: 42 additions & 12 deletions

File tree

tests/test_msg_ethereum_thorchain_deposit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def _build_deposit_calldata(memo):
2727
"""Build deposit(address,address,uint256,string) calldata (legacy selector)."""
2828
selector = bytes.fromhex("1fece7b4")
2929
vault = bytes(12) + bytes.fromhex(THOR_ROUTER)
30-
asset = bytes(12) + bytes.fromhex(ETH_NATIVE)
30+
asset = bytes(32) # address(0): the only native-ETH form the routers accept
3131
amount = (500000000000000000).to_bytes(32, "big") # 0.5 ETH
3232
memo_offset = (4 * 32).to_bytes(32, "big") # offset = 128
3333
memo_bytes = memo.encode("ascii")
@@ -41,7 +41,7 @@ def _build_deposit_with_expiry_calldata(memo, expiry=9999999999):
4141
"""Build depositWithExpiry(address,address,uint256,string,uint256) calldata."""
4242
selector = bytes.fromhex("44bc937b")
4343
vault = bytes(12) + bytes.fromhex(THOR_ROUTER)
44-
asset = bytes(12) + bytes.fromhex(ETH_NATIVE)
44+
asset = bytes(32) # address(0): the only native-ETH form the routers accept
4545
amount = (500000000000000000).to_bytes(32, "big") # 0.5 ETH
4646
memo_offset = (5 * 32).to_bytes(32, "big") # offset = 160 (after expiry)
4747
expiry_b = expiry.to_bytes(32, "big")

tests/test_msg_solana_signtx.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -263,31 +263,51 @@ def _build_tx(self, from_pubkey, accounts, program_id, instr_data, extra_account
263263
# ================================================================
264264

265265
def test_solana_sign_token_transfer(self):
266-
"""SPL Token transfer — OLED shows 'Send [amount] tokens to [address]'."""
266+
"""Unchecked SPL Transfer has no signed mint (the token being moved is
267+
not provable), so it now requires AdvancedMode (blind-sign); only the
268+
TransferChecked variant clear-signs."""
267269
self.requires_fullFeature()
268270
self.setup_mnemonic_allallall()
271+
from keepkeylib.client import CallException
269272
from_pubkey = self._get_from_pubkey()
270273
to_account = b'\x33' * 32 # destination token account
271-
owner = from_pubkey # token owner = signer
272274
# SPL Token Transfer instruction: opcode=3 (u8) + amount (LE u64)
273275
instr_data = bytes([3]) + struct.pack('<Q', 50000000) # 50M tokens
274276
raw_tx = self._build_tx(from_pubkey, [to_account], self.TOKEN_PROGRAM, instr_data)
275-
resp = self.client.call(messages.SolanaSignTx(
276-
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx))
277+
tx = messages.SolanaSignTx(
278+
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx)
279+
280+
self.client.apply_policy('AdvancedMode', False)
281+
with self.assertRaises(CallException):
282+
self.client.call(tx)
283+
284+
self.client.apply_policy('AdvancedMode', True)
285+
resp = self.client.call(tx)
277286
self.assertEqual(len(resp.signature), 64)
287+
self.client.apply_policy('AdvancedMode', False)
278288

279289
def test_solana_sign_token_approve(self):
280-
"""SPL Token approve — OLED shows 'Approve [amount] tokens to [delegate]'."""
290+
"""Unchecked SPL Approve hides the delegated token's mint, so it now
291+
requires AdvancedMode (blind-sign)."""
281292
self.requires_fullFeature()
282293
self.setup_mnemonic_allallall()
294+
from keepkeylib.client import CallException
283295
from_pubkey = self._get_from_pubkey()
284296
delegate = b'\x44' * 32
285297
# SPL Token Approve: opcode=4 (u8) + amount (LE u64)
286298
instr_data = bytes([4]) + struct.pack('<Q', 100000000)
287299
raw_tx = self._build_tx(from_pubkey, [delegate], self.TOKEN_PROGRAM, instr_data)
288-
resp = self.client.call(messages.SolanaSignTx(
289-
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx))
300+
tx = messages.SolanaSignTx(
301+
address_n=parse_path("m/44'/501'/0'/0'"), raw_tx=raw_tx)
302+
303+
self.client.apply_policy('AdvancedMode', False)
304+
with self.assertRaises(CallException):
305+
self.client.call(tx)
306+
307+
self.client.apply_policy('AdvancedMode', True)
308+
resp = self.client.call(tx)
290309
self.assertEqual(len(resp.signature), 64)
310+
self.client.apply_policy('AdvancedMode', False)
291311

292312
def test_solana_sign_stake_delegate(self):
293313
"""Stake delegate — OLED shows 'Delegate stake?'."""
@@ -542,9 +562,12 @@ def test_solana_sign_multi_instruction_transfer_and_memo(self):
542562
# ================================================================
543563

544564
def test_solana_sign_token_transfer_with_metadata(self):
545-
"""SPL Token transfer with SolanaTokenInfo for OLED display of symbol + decimals."""
565+
"""Host SolanaTokenInfo does NOT make an unchecked transfer clear-signable:
566+
the mint is not signed, so the metadata is unauthenticated and the tx
567+
still requires AdvancedMode. (TransferChecked binds the mint on-chain.)"""
546568
self.requires_fullFeature()
547569
self.setup_mnemonic_allallall()
570+
from keepkeylib.client import CallException
548571

549572
from_pubkey = self._get_from_pubkey()
550573
to_account = b'\x33' * 32 # destination token account
@@ -567,13 +590,20 @@ def test_solana_sign_token_transfer_with_metadata(self):
567590
decimals=6,
568591
)
569592

570-
resp = self.client.call(messages.SolanaSignTx(
593+
tx = messages.SolanaSignTx(
571594
address_n=parse_path("m/44'/501'/0'/0'"),
572595
raw_tx=raw_tx,
573596
token_info=[token_info],
574-
))
597+
)
598+
self.client.apply_policy('AdvancedMode', False)
599+
with self.assertRaises(CallException):
600+
self.client.call(tx)
601+
602+
self.client.apply_policy('AdvancedMode', True)
603+
resp = self.client.call(tx)
575604
self.assertEqual(len(resp.signature), 64)
576605
self.assertFalse(all(b == 0 for b in resp.signature))
606+
self.client.apply_policy('AdvancedMode', False)
577607

578608
# ================================================================
579609
# Path edge-case tests

0 commit comments

Comments
 (0)