Skip to content

feat: full TSS recovery flow for the Tempo chain (TIP-20 / EIP-7702#8741

Open
bhavesh-prasad wants to merge 3 commits into
masterfrom
CECHO-917
Open

feat: full TSS recovery flow for the Tempo chain (TIP-20 / EIP-7702#8741
bhavesh-prasad wants to merge 3 commits into
masterfrom
CECHO-917

Conversation

@bhavesh-prasad
Copy link
Copy Markdown
Contributor

@bhavesh-prasad bhavesh-prasad commented May 12, 2026

Summary

Implements the full TSS recovery flow for the Tempo chain (TIP-20 / EIP-7702).

What was there before

  • buildUnsignedSweepTxnTSS and recoveryBlockchainExplorerQuery were stubs returning empty/dummy objects
  • No signed recovery path existed

What this PR adds

Recovery infrastructure (tempo.ts)

  • recoveryBlockchainExplorerQuery — routes Etherscan-style queries to Tempo's Alchemy JSON-RPC endpoint (configured via environments.ts)
  • queryTempoRpc — translates account/balance, account/tokenbalance, and account/txlist queries into eth_call / eth_getTransactionCount JSON-RPC calls
  • buildTempoSweepTx — shared helper that queries token balance, computes sweep amount, and builds an unsigned TIP-20 transfer transaction; used by both recovery paths
  • recoverTSS — overrides base-class recovery with two paths:
    • Unsigned sweep (plain public key shares): delegates to buildUnsignedSweepTxnTSS
    • Signed sweep (encrypted keys + passphrase): builds a TIP-20 tx, hashes it, signs via MPC v2 ECDSA, attaches the signature, and returns the broadcast-ready hex
  • buildUnsignedSweepTxnTSS — builds an unsigned TIP-20 sweep and returns the UnsignedSweepTxMPCv2 structure for offline vault signing

Dynamic gas margin

  • Removes hardcoded GAS_MARGIN_UNITS = 20_000n constant
  • Gas margin is now computed from user-supplied params: gasMarginUnits = gasLimit × maxFeePerGas / 10^12
  • Matches the gasLimit × gasPrice pattern used across all ETH-like coin recovery flows in abstract-eth; the /10^12 factor converts from wei scale (18 decimals) to pathUSD's TIP-20 units (6 decimals)

Environment config (environments.ts)

  • Adds Tempo evm config entry so recoveryBlockchainExplorerQuery can resolve the RPC URL and API token

@bhavesh-prasad bhavesh-prasad requested review from a team as code owners May 12, 2026 09:16
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 12, 2026

CECHO-917

@bhavesh-prasad bhavesh-prasad force-pushed the CECHO-917 branch 4 times, most recently from cfd5179 to f1ccfe6 Compare May 13, 2026 09:49
Signed-off-by: B.Prasad <bhaveshprasad584@bitgo.com>

TICKET: CECHO-917
Signed-off-by: B.Prasad <bhaveshprasad584@bitgo.com>
@bhavesh-prasad bhavesh-prasad requested a review from a team as a code owner May 13, 2026 10:23
@bhavesh-prasad bhavesh-prasad changed the title feat: dynamic gas margin for Tempo recovery based on user gas params feat: full TSS recovery flow for the Tempo chain (TIP-20 / EIP-7702 May 13, 2026
Comment thread modules/sdk-coin-tempo/src/tempo.ts Outdated
Comment thread modules/sdk-coin-tempo/src/tempo.ts Outdated
Copy link
Copy Markdown
Contributor

@nvrakesh06 nvrakesh06 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Copy link
Copy Markdown
Contributor

@nvrakesh06 nvrakesh06 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Copy link
Copy Markdown
Contributor

@nvrakesh06 nvrakesh06 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fee estimation — two cases

Case A — swept token == fee token ✅ handled

When sweeping pathUSD (the fee token), the gas cost is correctly reserved before sweeping:

const sweepAmount = isPathUsd ? balance - gasMarginUnits : balance;
if (sweepAmount <= 0n) { throw ... }

The function throws early if the balance doesn't cover the fee, so the wallet is never left unable to pay.


Case B — swept token != fee token ❌ not handled

When sweeping a different TIP-20 (e.g. tempo:usdc), the full token balance is swept but the fee token balance is never checked. If the wallet has no pathUSD, the transaction will be submitted and fail on-chain silently.

The same guard from Case A needs to be applied to the fee token balance here:

if (feeTokenAddress.toLowerCase() !== tokenAddress.toLowerCase()) {
  const feeTokenRaw = await this.queryAddressTokenBalance(feeTokenAddress, walletAddress, params.apiKey);
  if (BigInt(feeTokenRaw.toString()) < gasMarginUnits) {
    throw new Error(
      `Insufficient fee token balance in ${feeTokenAddress}: ` +
      `have ${feeTokenRaw} units, need at least ${gasMarginUnits} units for gas`
    );
  }
}

…% gas buffer

TEST_BACKUP_KEY updated to a full 130-char commonKeyChain (pubkey + chaincode)
since buildUnsignedSweepTxnTSS now calls MPC.deriveUnhardened which requires
both components. Test 2 balance raised from 15_000 to 50_000 to account for
the 75% gas margin buffer raising the minimum required balance to 17_500.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

TICKET: CECHO-917
Signed-off-by: B.Prasad <bhaveshprasad584@bitgo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants