Skip to content

Commit 58a4b51

Browse files
committed
feat(sdk-coin-tempo): add token transaction builder
TICKET: WIN-8479
1 parent 20af49e commit 58a4b51

File tree

13 files changed

+1118
-56
lines changed

13 files changed

+1118
-56
lines changed

modules/sdk-coin-tempo/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
"@bitgo/abstract-eth": "^24.19.4",
4444
"@bitgo/sdk-core": "^36.25.0",
4545
"@bitgo/secp256k1": "^1.8.0",
46-
"@bitgo/statics": "^58.19.0"
46+
"@bitgo/statics": "^58.19.0",
47+
"viem": "^2.21.0"
4748
},
4849
"devDependencies": {
4950
"@bitgo/sdk-api": "^1.72.2",

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,25 @@
55
export const MAINNET_COIN = 'tempo';
66
export const TESTNET_COIN = 'ttempo';
77

8-
export const VALID_ADDRESS_REGEX = /^[A-Za-z0-9]+$/; // Update with actual address format
9-
export const VALID_PUBLIC_KEY_REGEX = /^[A-Fa-f0-9]{64}$/; // Update with actual public key format
8+
export const VALID_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
9+
export const VALID_PUBLIC_KEY_REGEX = /^[A-Fa-f0-9]{64}$/;
10+
11+
/**
12+
* Tempo Chain IDs
13+
*/
14+
export const TEMPO_CHAIN_IDS = {
15+
TESTNET: 42429, // Andantino testnet
16+
// MAINNET: TBD
17+
} as const;
18+
19+
/**
20+
* TIP-20 Token Standard
21+
* TIP-20 uses 6 decimals (unlike ERC-20's standard 18 decimals)
22+
*/
23+
export const TIP20_DECIMALS = 6;
24+
25+
/**
26+
* AA Transaction Type
27+
* Tempo uses EIP-7702 Account Abstraction with transaction type 0x76
28+
*/
29+
export const AA_TRANSACTION_TYPE = '0x76' as const;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ export * from './utils';
33
export * from './constants';
44
export * from './iface';
55
export * from './tip20Abi';
6+
export * from './types';
7+
export * from './transactionBuilder';
8+
export * from './transaction';
Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,60 @@
11
/**
2-
* TIP20 Token Standard ABI (Skeleton)
2+
* TIP-20 Token Standard ABI
33
*
4-
* TODO: Update this file when TIP20 ABI becomes available
4+
* TIP-20 is Tempo's token standard, similar to ERC-20 but with:
5+
* - 6 decimal places (instead of 18)
6+
* - transferWithMemo function for attaching metadata
57
*/
68

79
/**
8-
* Placeholder TIP20 ABI
9-
* This is an empty array that should be replaced with the actual ABI
10+
* TIP-20 transferWithMemo ABI
11+
* Standard function for TIP-20 token transfers with memo field
1012
*/
11-
export const TIP20_ABI = [] as const;
13+
export const TIP20_TRANSFER_WITH_MEMO_ABI = [
14+
{
15+
type: 'function',
16+
name: 'transferWithMemo',
17+
inputs: [
18+
{ name: 'to', type: 'address' },
19+
{ name: 'amount', type: 'uint256' },
20+
{ name: 'memo', type: 'bytes32' },
21+
],
22+
outputs: [{ name: '', type: 'bool' }],
23+
stateMutability: 'nonpayable',
24+
},
25+
] as const;
1226

1327
/**
14-
* Placeholder for TIP20 Factory ABI
28+
* Standard TIP-20 token ABI (similar to ERC-20)
1529
*/
16-
export const TIP20_FACTORY_ABI = [] as const;
17-
18-
/**
19-
* Get the method signature for TIP20 transfer
20-
* TODO: Update with actual method name if different from ERC20
21-
*/
22-
export function getTip20TransferSignature(): string {
23-
return 'transfer(address,uint256)';
24-
}
25-
26-
/**
27-
* Get the method signature for TIP20 transferFrom
28-
* TODO: Update with actual method name if different from ERC20
29-
*/
30-
export function getTip20TransferFromSignature(): string {
31-
return 'transferFrom(address,address,uint256)';
32-
}
30+
export const TIP20_ABI = [
31+
{
32+
type: 'function',
33+
name: 'balanceOf',
34+
inputs: [{ name: 'account', type: 'address' }],
35+
outputs: [{ name: '', type: 'uint256' }],
36+
stateMutability: 'view',
37+
},
38+
{
39+
type: 'function',
40+
name: 'transfer',
41+
inputs: [
42+
{ name: 'to', type: 'address' },
43+
{ name: 'amount', type: 'uint256' },
44+
],
45+
outputs: [{ name: '', type: 'bool' }],
46+
stateMutability: 'nonpayable',
47+
},
48+
{
49+
type: 'function',
50+
name: 'transferFrom',
51+
inputs: [
52+
{ name: 'from', type: 'address' },
53+
{ name: 'to', type: 'address' },
54+
{ name: 'amount', type: 'uint256' },
55+
],
56+
outputs: [{ name: '', type: 'bool' }],
57+
stateMutability: 'nonpayable',
58+
},
59+
...TIP20_TRANSFER_WITH_MEMO_ABI,
60+
] as const;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* TIP-20 Transaction
3+
*
4+
* Represents a Tempo Account Abstraction (AA) transaction (type 0x76)
5+
* Supports single or batch TIP-20 token transfers with memos
6+
*/
7+
8+
import { BaseTransaction, ParseTransactionError, TransactionType } from '@bitgo/sdk-core';
9+
import { BaseCoin as CoinConfig } from '@bitgo/statics';
10+
import type { Address, Hex } from 'viem';
11+
import { Tip20Operation } from './types';
12+
13+
/**
14+
* TIP-20 Transaction Request Structure
15+
* Represents the raw transaction data for Tempo Account Abstraction (EIP-7702)
16+
*/
17+
export interface Tip20TransactionRequest {
18+
/** Transaction type (0x76 for Tempo AA) */
19+
type: number | string;
20+
/** Chain ID for the Tempo network */
21+
chainId: number;
22+
/** Transaction nonce */
23+
nonce: number;
24+
/** Maximum fee per gas (EIP-1559) */
25+
maxFeePerGas: bigint;
26+
/** Maximum priority fee per gas (EIP-1559) */
27+
maxPriorityFeePerGas: bigint;
28+
/** Gas limit for the transaction */
29+
gas: bigint;
30+
/** Array of calls to execute in this transaction */
31+
calls: { to: Address; data: Hex; value: bigint }[];
32+
/** Access list (optional, typically empty for TIP-20) */
33+
accessList?: unknown[];
34+
/** Optional TIP-20 token to use for paying fees */
35+
feeToken?: Address;
36+
}
37+
38+
export class Tip20Transaction extends BaseTransaction {
39+
private txRequest: Tip20TransactionRequest;
40+
private _operations: Tip20Operation[];
41+
private _signature?: { r: Hex; s: Hex; yParity: number };
42+
43+
constructor(_coinConfig: Readonly<CoinConfig>, request: Tip20TransactionRequest, operations: Tip20Operation[] = []) {
44+
super(_coinConfig);
45+
this.txRequest = request;
46+
this._operations = operations;
47+
}
48+
49+
get type(): TransactionType {
50+
return TransactionType.Send;
51+
}
52+
53+
canSign(): boolean {
54+
return true;
55+
}
56+
57+
async serialize(signature?: { r: Hex; s: Hex; yParity: number }): Promise<Hex> {
58+
// TODO: Implement viem EIP-7702 transaction serialization
59+
throw new ParseTransactionError('Transaction serialization not yet implemented');
60+
}
61+
62+
getOperations(): Tip20Operation[] {
63+
return [...this._operations];
64+
}
65+
66+
getFeeToken(): Address | undefined {
67+
return this.txRequest.feeToken;
68+
}
69+
70+
getOperationCount(): number {
71+
return this.txRequest.calls.length;
72+
}
73+
74+
isBatch(): boolean {
75+
return this.txRequest.calls.length > 1;
76+
}
77+
78+
setSignature(signature: { r: Hex; s: Hex; yParity: number }): void {
79+
this._signature = signature;
80+
}
81+
82+
getSignature(): { r: Hex; s: Hex; yParity: number } | undefined {
83+
return this._signature;
84+
}
85+
86+
toJson(): Record<string, unknown> {
87+
return {
88+
type: this.txRequest.type,
89+
chainId: this.txRequest.chainId,
90+
nonce: this.txRequest.nonce,
91+
maxFeePerGas: this.txRequest.maxFeePerGas.toString(),
92+
maxPriorityFeePerGas: this.txRequest.maxPriorityFeePerGas.toString(),
93+
gas: this.txRequest.gas.toString(),
94+
callCount: this.txRequest.calls.length,
95+
feeToken: this.txRequest.feeToken,
96+
operations: this._operations,
97+
signature: this._signature,
98+
};
99+
}
100+
101+
async toBroadcastFormat(): Promise<string> {
102+
return await this.serialize(this._signature);
103+
}
104+
105+
get id(): string {
106+
return 'pending';
107+
}
108+
109+
toString(): string {
110+
return JSON.stringify(this.toJson(), null, 2);
111+
}
112+
113+
canBroadcast(): boolean {
114+
return this.txRequest.calls.length > 0 && this.txRequest.chainId > 0;
115+
}
116+
}

0 commit comments

Comments
 (0)