Skip to content

Commit bca6c93

Browse files
committed
feat(sdk-coin-tempo): implement Tempo recovery functionality
Ticket: CECHO-625
1 parent 2cc3efb commit bca6c93

9 files changed

Lines changed: 717 additions & 16 deletions

File tree

modules/sdk-coin-tempo/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
},
4242
"dependencies": {
4343
"@bitgo/abstract-eth": "^24.22.0",
44+
"@bitgo/sdk-lib-mpc": "^10.10.0",
4445
"@bitgo/sdk-core": "^36.37.0",
4546
"@bitgo/secp256k1": "^1.11.0",
4647
"@bitgo/statics": "^58.32.0",

modules/sdk-coin-tempo/src/lib/constants.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,14 @@ export const TIP20_DECIMALS = 6;
2727
* Tempo uses EIP-7702 Account Abstraction with transaction type 0x76
2828
*/
2929
export const AA_TRANSACTION_TYPE = '0x76' as const;
30+
31+
/**
32+
* Fallback JSON-RPC endpoints when `common.Environments[bitgo.getEnv()].evm.tempo|ttempo.rpcUrl`
33+
* is missing (should not happen for normal envs). Primary RPC config lives in sdk-core
34+
* `environments.ts` under `evm.tempo` / `evm.ttempo`; `@bitgo/statics` networks only define
35+
* explorer URLs and chainId, not RPC.
36+
*/
37+
export const TEMPO_RPC_URLS = {
38+
MAINNET: 'https://rpc.mainnet.tempo.xyz',
39+
TESTNET: 'https://rpc.testnet.tempo.xyz',
40+
} as const;

modules/sdk-coin-tempo/src/lib/iface.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
/**
22
* Interfaces for Tempo
33
*/
4+
import type { RecoverOptions } from '@bitgo/abstract-eth';
5+
6+
/**
7+
* Optional Tempo-specific recovery fields (passed on {@link RecoverOptions}).
8+
* Fees on Tempo are paid in a TIP-20 token; defaults to the recovered token.
9+
*/
10+
export type TempoRecoveryOptions = RecoverOptions & {
11+
/** TIP-20 contract used to pay AA gas (defaults to token being swept) */
12+
feeTokenAddress?: string;
13+
/** Override default public RPC URL */
14+
rpcUrl?: string;
15+
};
416

517
// eslint-disable-next-line @typescript-eslint/no-empty-interface
618
export interface TransactionData {

modules/sdk-coin-tempo/src/lib/utils.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
import { bip32 } from '@bitgo/secp256k1';
88
import { ethers } from 'ethers';
9-
import { AA_TRANSACTION_TYPE, TIP20_DECIMALS } from './constants';
10-
import { TIP20_TRANSFER_WITH_MEMO_ABI } from './tip20Abi';
9+
import { AA_TRANSACTION_TYPE, TEMPO_RPC_URLS, TIP20_DECIMALS } from './constants';
10+
import { TIP20_ABI, TIP20_TRANSFER_WITH_MEMO_ABI } from './tip20Abi';
1111

1212
const AA_TX_HEX_REGEX = new RegExp(`^${AA_TRANSACTION_TYPE}[0-9a-f]*$`, 'i');
1313

@@ -149,6 +149,37 @@ export function isValidMemoId(memoId: string): boolean {
149149
return typeof memoId === 'string' && /^(0|[1-9]\d*)$/.test(memoId);
150150
}
151151

152+
/**
153+
* Resolve default Tempo JSON-RPC URL from base chain name.
154+
*/
155+
export function getTempoRpcUrlForBaseChain(baseChain: string): string {
156+
return baseChain === 'ttempo' ? TEMPO_RPC_URLS.TESTNET : TEMPO_RPC_URLS.MAINNET;
157+
}
158+
159+
/**
160+
* Query TIP-20 balance via standard `balanceOf` eth_call.
161+
*/
162+
export async function queryTip20TokenBalance(
163+
rpcUrl: string,
164+
tokenContractAddress: string,
165+
walletAddress: string
166+
): Promise<bigint> {
167+
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
168+
const iface = new ethers.utils.Interface(TIP20_ABI);
169+
const data = iface.encodeFunctionData('balanceOf', [walletAddress]);
170+
const result = await provider.call({ to: ethers.utils.getAddress(tokenContractAddress), data });
171+
const [bal] = iface.decodeFunctionResult('balanceOf', result);
172+
return BigInt(bal.toString());
173+
}
174+
175+
/**
176+
* Pending nonce for an address (for AA / account tx ordering).
177+
*/
178+
export async function getTempoAddressNonce(rpcUrl: string, address: string): Promise<number> {
179+
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
180+
return provider.getTransactionCount(ethers.utils.getAddress(address), 'pending');
181+
}
182+
152183
const utils = {
153184
isValidAddress,
154185
isValidPublicKey,
@@ -160,6 +191,9 @@ const utils = {
160191
isValidTip20Amount,
161192
isTip20Transaction,
162193
isValidMemoId,
194+
getTempoRpcUrlForBaseChain,
195+
queryTip20TokenBalance,
196+
getTempoAddressNonce,
163197
};
164198

165199
export default utils;

0 commit comments

Comments
 (0)