Skip to content

Commit 347c2c6

Browse files
committed
feat(sdk-coin-tempo): add token transaction builder
TICKET: WIN-8479
1 parent 16adf9f commit 347c2c6

File tree

12 files changed

+1084
-48
lines changed

12 files changed

+1084
-48
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: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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+
export class Tip20Transaction extends BaseTransaction {
14+
private txRequest: any;
15+
private _operations: Tip20Operation[];
16+
private _signature?: { r: Hex; s: Hex; yParity: number };
17+
18+
constructor(_coinConfig: Readonly<CoinConfig>, request: any, operations: Tip20Operation[] = []) {
19+
super(_coinConfig);
20+
this.txRequest = request;
21+
this._operations = operations;
22+
}
23+
24+
get type(): TransactionType {
25+
return TransactionType.Send;
26+
}
27+
28+
canSign(): boolean {
29+
return true;
30+
}
31+
32+
async serialize(signature?: { r: Hex; s: Hex; yParity: number }): Promise<Hex> {
33+
// TODO: Implement viem EIP-7702 transaction serialization
34+
throw new ParseTransactionError('Transaction serialization not yet implemented');
35+
}
36+
37+
getOperations(): Tip20Operation[] {
38+
return [...this._operations];
39+
}
40+
41+
getFeeToken(): Address | undefined {
42+
return this.txRequest.feeToken;
43+
}
44+
45+
getOperationCount(): number {
46+
return this.txRequest.calls.length;
47+
}
48+
49+
isBatch(): boolean {
50+
return this.txRequest.calls.length > 1;
51+
}
52+
53+
setSignature(signature: { r: Hex; s: Hex; yParity: number }): void {
54+
this._signature = signature;
55+
}
56+
57+
getSignature(): { r: Hex; s: Hex; yParity: number } | undefined {
58+
return this._signature;
59+
}
60+
61+
toJson(): Record<string, unknown> {
62+
return {
63+
type: this.txRequest.type,
64+
chainId: this.txRequest.chainId,
65+
nonce: this.txRequest.nonce,
66+
maxFeePerGas: this.txRequest.maxFeePerGas.toString(),
67+
maxPriorityFeePerGas: this.txRequest.maxPriorityFeePerGas.toString(),
68+
gas: this.txRequest.gas.toString(),
69+
callCount: this.txRequest.calls.length,
70+
feeToken: this.txRequest.feeToken,
71+
operations: this._operations,
72+
signature: this._signature,
73+
};
74+
}
75+
76+
async toBroadcastFormat(): Promise<string> {
77+
return await this.serialize(this._signature);
78+
}
79+
80+
get id(): string {
81+
return 'pending';
82+
}
83+
84+
toString(): string {
85+
return JSON.stringify(this.toJson(), null, 2);
86+
}
87+
88+
canBroadcast(): boolean {
89+
return this.txRequest.calls.length > 0 && this.txRequest.chainId > 0;
90+
}
91+
}

0 commit comments

Comments
 (0)