Skip to content

Commit 305d75f

Browse files
feat(sdk-coin-ton): add jetton skeleton
Ticket: WIN-3198 TICKET: WIN-3198
1 parent 46fd5c6 commit 305d75f

File tree

6 files changed

+267
-1
lines changed

6 files changed

+267
-1
lines changed

modules/bitgo/src/v2/coinFactory.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { AlgoToken } from '@bitgo/sdk-coin-algo';
66
import { Bcha, Tbcha } from '@bitgo/sdk-coin-bcha';
77
import { HbarToken } from '@bitgo/sdk-coin-hbar';
88
import { Near, TNear, Nep141Token } from '@bitgo/sdk-coin-near';
9+
import { TonToken } from '@bitgo/sdk-coin-ton';
910
import { SolToken } from '@bitgo/sdk-coin-sol';
1011
import { TrxToken } from '@bitgo/sdk-coin-trx';
1112
import { CoinFactory, CoinConstructor } from '@bitgo/sdk-core';
@@ -36,6 +37,7 @@ import {
3637
VetTokenConfig,
3738
TaoTokenConfig,
3839
PolyxTokenConfig,
40+
TonTokenConfig,
3941
} from '@bitgo/statics';
4042
import {
4143
Ada,
@@ -532,6 +534,10 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin
532534
VetToken.createTokenConstructors().forEach(({ name, coinConstructor }) =>
533535
coinFactory.register(name, coinConstructor)
534536
);
537+
538+
TonToken.createTokenConstructors([...tokens.bitcoin.ton.tokens, ...tokens.testnet.ton.tokens]).forEach(
539+
({ name, coinConstructor }) => coinFactory.register(name, coinConstructor)
540+
);
535541
}
536542

537543
export function getCoinConstructor(coinName: string): CoinConstructor | undefined {
@@ -983,6 +989,9 @@ export function getTokenConstructor(tokenConfig: TokenConfig): CoinConstructor |
983989
case 'zeta':
984990
case 'tzeta':
985991
return CosmosToken.createTokenConstructor(tokenConfig as CosmosTokenConfig);
992+
case 'ton':
993+
case 'tton':
994+
return TonToken.createTokenConstructor(tokenConfig as TonTokenConfig);
986995
default:
987996
return undefined;
988997
}

modules/sdk-coin-ton/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './lib';
22
export * from './register';
33
export * from './ton';
44
export * from './tton';
5+
export * from './tonToken';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import { BitGoBase } from '@bitgo/sdk-core';
22
import { Ton } from './ton';
33
import { Tton } from './tton';
4+
import { TonToken } from './tonToken';
45

56
export const register = (sdk: BitGoBase): void => {
67
sdk.register('ton', Ton.createInstance);
78
sdk.register('tton', Tton.createInstance);
9+
10+
// Register Jetton tokens
11+
const tokens = TonToken.createTokenConstructors();
12+
tokens.forEach((token) => {
13+
sdk.register(token.name, token.coinConstructor);
14+
});
815
};
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { BitGoBase, CoinConstructor, NamedCoinConstructor, VerifyTransactionOptions } from '@bitgo/sdk-core';
2+
import BigNumber from 'bignumber.js';
3+
import { coins, TonTokenConfig, NetworkType, tokens } from '@bitgo/statics';
4+
5+
import { Transaction } from './lib';
6+
import { Ton } from './ton';
7+
8+
export class TonToken extends Ton {
9+
public readonly tokenConfig: TonTokenConfig;
10+
11+
constructor(bitgo: BitGoBase, tokenConfig: TonTokenConfig) {
12+
const staticsCoin = tokenConfig.network === NetworkType.MAINNET ? coins.get('ton') : coins.get('tton');
13+
super(bitgo, staticsCoin);
14+
this.tokenConfig = tokenConfig;
15+
}
16+
17+
static createTokenConstructor(config: TonTokenConfig): CoinConstructor {
18+
return (bitgo: BitGoBase) => new TonToken(bitgo, config);
19+
}
20+
21+
static createTokenConstructors(
22+
tokenConfig: TonTokenConfig[] = [...tokens.bitcoin.ton.tokens, ...tokens.testnet.ton.tokens]
23+
): NamedCoinConstructor[] {
24+
const tokensCtors: NamedCoinConstructor[] = [];
25+
for (const token of tokenConfig) {
26+
const tokenConstructor = TonToken.createTokenConstructor(token);
27+
tokensCtors.push({ name: token.type, coinConstructor: tokenConstructor });
28+
}
29+
return tokensCtors;
30+
}
31+
32+
get name(): string {
33+
return this.tokenConfig.name;
34+
}
35+
36+
get coin(): string {
37+
return this.tokenConfig.coin;
38+
}
39+
40+
get jettonMaster(): string {
41+
return this.tokenConfig.jettonMaster;
42+
}
43+
44+
get decimalPlaces(): number {
45+
return this.tokenConfig.decimalPlaces;
46+
}
47+
48+
getChain(): string {
49+
return this.tokenConfig.type;
50+
}
51+
52+
getBaseChain(): string {
53+
return this.coin;
54+
}
55+
56+
getFullName(): string {
57+
return 'TON Token';
58+
}
59+
60+
getBaseFactor(): number {
61+
return Math.pow(10, this.tokenConfig.decimalPlaces);
62+
}
63+
64+
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
65+
const { txPrebuild: txPrebuild, txParams: txParams } = params;
66+
const rawTx = txPrebuild.txHex;
67+
let totalAmount = new BigNumber(0);
68+
if (!rawTx) {
69+
throw new Error('missing required tx prebuild property txHex');
70+
}
71+
const coinConfig = coins.get(this.getChain());
72+
const transaction = new Transaction(coinConfig);
73+
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
74+
const explainedTx = transaction.explainTransaction();
75+
if (txParams.recipients !== undefined) {
76+
txParams.recipients.forEach((recipient) => {
77+
if (recipient.tokenName && recipient.tokenName !== coinConfig.name) {
78+
throw new Error('incorrect token name specified in recipients');
79+
}
80+
recipient.tokenName = coinConfig.name;
81+
});
82+
const filteredRecipients = txParams.recipients?.map((recipient) => ({
83+
address: recipient.address,
84+
amount: recipient.amount,
85+
tokenName: recipient.tokenName,
86+
}));
87+
const filteredOutputs = explainedTx.outputs.map((output) => ({
88+
address: output.address,
89+
amount: output.amount,
90+
tokenName: output.tokenName,
91+
}));
92+
const outputsMatch = JSON.stringify(filteredRecipients) === JSON.stringify(filteredOutputs);
93+
if (!outputsMatch) {
94+
throw new Error('Tx outputs does not match with expected txParams recipients');
95+
}
96+
for (const recipient of txParams.recipients) {
97+
totalAmount = totalAmount.plus(recipient.amount);
98+
}
99+
if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
100+
throw new Error('Tx total amount does not match with expected total amount field');
101+
}
102+
}
103+
return true;
104+
}
105+
}

modules/statics/src/account.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ export interface Nep141TokenConstructorOptions extends AccountConstructorOptions
158158
storageDepositAmount: string;
159159
}
160160

161+
export interface TonTokenConstructorOptions extends AccountConstructorOptions {
162+
jettonMaster: string;
163+
}
164+
161165
export interface VetTokenConstructorOptions extends AccountConstructorOptions {
162166
contractAddress: string;
163167
gasTankToken?: string;
@@ -635,6 +639,18 @@ export class Nep141Token extends AccountCoinToken {
635639
}
636640
}
637641

642+
export class TonToken extends AccountCoinToken {
643+
public jettonMaster: string;
644+
645+
constructor(options: TonTokenConstructorOptions) {
646+
super({
647+
...options,
648+
});
649+
650+
this.jettonMaster = options.jettonMaster;
651+
}
652+
}
653+
638654
export class VetToken extends AccountCoinToken {
639655
public contractAddress: string;
640656
public gasTankToken?: string;
@@ -3091,6 +3107,95 @@ export function sip10Token(
30913107
* @param network? Optional token network. Defaults to the testnet Stacks network.
30923108
* @param features? Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin`
30933109
*/
3110+
/**
3111+
* Factory function for TON token instances.
3112+
*
3113+
* @param id uuid v4
3114+
* @param name unique identifier of the token
3115+
* @param fullName Complete human-readable name of the token
3116+
* @param decimalPlaces Number of decimal places this token supports (divisibility exponent)
3117+
* @param jettonMaster Jetton master address of this token
3118+
* @param asset Asset which this coin represents. This is the same for both mainnet and testnet variants of a coin.
3119+
* @param features Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin`
3120+
* @param prefix Optional token prefix. Defaults to empty string
3121+
* @param suffix Optional token suffix. Defaults to token name.
3122+
* @param network Optional token network. Defaults to TON main network.
3123+
* @param primaryKeyCurve The elliptic curve for this chain/token
3124+
*/
3125+
export function tonToken(
3126+
id: string,
3127+
name: string,
3128+
fullName: string,
3129+
decimalPlaces: number,
3130+
jettonMaster: string,
3131+
asset: UnderlyingAsset,
3132+
features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES,
3133+
prefix = '',
3134+
suffix: string = name.toUpperCase(),
3135+
network: AccountNetwork = Networks.main.ton,
3136+
primaryKeyCurve: KeyCurve = KeyCurve.Ed25519
3137+
) {
3138+
return Object.freeze(
3139+
new TonToken({
3140+
id,
3141+
name,
3142+
fullName,
3143+
network,
3144+
jettonMaster,
3145+
prefix,
3146+
suffix,
3147+
features,
3148+
decimalPlaces,
3149+
asset,
3150+
isToken: true,
3151+
primaryKeyCurve,
3152+
baseUnit: BaseUnit.TON,
3153+
})
3154+
);
3155+
}
3156+
3157+
/**
3158+
* Factory function for testnet TON token instances.
3159+
*
3160+
* @param id uuid v4
3161+
* @param name unique identifier of the token
3162+
* @param fullName Complete human-readable name of the token
3163+
* @param decimalPlaces Number of decimal places this token supports (divisibility exponent)
3164+
* @param jettonMaster Jetton master address of this token
3165+
* @param asset Asset which this coin represents. This is the same for both mainnet and testnet variants of a coin.
3166+
* @param features Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin`
3167+
* @param prefix Optional token prefix. Defaults to empty string
3168+
* @param suffix Optional token suffix. Defaults to token name.
3169+
* @param network Optional token network. Defaults to the testnet TON network.
3170+
*/
3171+
export function ttonToken(
3172+
id: string,
3173+
name: string,
3174+
fullName: string,
3175+
decimalPlaces: number,
3176+
jettonMaster: string,
3177+
asset: UnderlyingAsset,
3178+
features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES,
3179+
prefix = '',
3180+
suffix: string = name.toUpperCase(),
3181+
network: AccountNetwork = Networks.test.ton,
3182+
primaryKeyCurve: KeyCurve = KeyCurve.Ed25519
3183+
) {
3184+
return tonToken(
3185+
id,
3186+
name,
3187+
fullName,
3188+
decimalPlaces,
3189+
jettonMaster,
3190+
asset,
3191+
features,
3192+
prefix,
3193+
suffix,
3194+
network,
3195+
primaryKeyCurve
3196+
);
3197+
}
3198+
30943199
export function tsip10Token(
30953200
id: string,
30963201
name: string,

modules/statics/src/tokenConfig.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
Erc20Coin,
1616
Erc721Coin,
1717
HederaToken,
18+
TonToken,
1819
Nep141Token,
1920
OpethERC20Token,
2021
PolygonERC20Token,
@@ -126,6 +127,10 @@ export type Nep141TokenConfig = BaseNetworkConfig & {
126127
storageDepositAmount: string;
127128
};
128129

130+
export type TonTokenConfig = BaseNetworkConfig & {
131+
jettonMaster: string;
132+
};
133+
129134
export type VetTokenConfig = BaseNetworkConfig & {
130135
contractAddress: string;
131136
};
@@ -156,7 +161,8 @@ export type TokenConfig =
156161
| CosmosTokenConfig
157162
| VetTokenConfig
158163
| TaoTokenConfig
159-
| PolyxTokenConfig;
164+
| PolyxTokenConfig
165+
| TonTokenConfig;
160166

161167
export interface Tokens {
162168
bitcoin: {
@@ -249,6 +255,9 @@ export interface Tokens {
249255
cosmos: {
250256
tokens: CosmosTokenConfig[];
251257
};
258+
ton: {
259+
tokens: TonTokenConfig[];
260+
};
252261
};
253262
testnet: {
254263
eth: {
@@ -340,6 +349,9 @@ export interface Tokens {
340349
cosmos: {
341350
tokens: CosmosTokenConfig[];
342351
};
352+
ton: {
353+
tokens: TonTokenConfig[];
354+
};
343355
};
344356
}
345357

@@ -995,6 +1007,25 @@ function getCosmosTokenConfig(coin: CosmosChainToken): CosmosTokenConfig {
9951007
};
9961008
}
9971009

1010+
function getTonTokenConfig(coin: TonToken): TonTokenConfig {
1011+
return {
1012+
type: coin.name,
1013+
coin: coin.network.type === NetworkType.MAINNET ? 'ton' : 'tton',
1014+
network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
1015+
name: coin.fullName,
1016+
jettonMaster: coin.jettonMaster,
1017+
decimalPlaces: coin.decimalPlaces,
1018+
};
1019+
}
1020+
1021+
const getFormattedTonTokens = (customCoinMap = coins) =>
1022+
customCoinMap.reduce((acc: TonTokenConfig[], coin) => {
1023+
if (coin instanceof TonToken) {
1024+
acc.push(getTonTokenConfig(coin));
1025+
}
1026+
return acc;
1027+
}, []);
1028+
9981029
export const getFormattedTokens = (coinMap = coins): Tokens => {
9991030
const formattedAptNFTCollections = getFormattedAptNFTCollections(coinMap);
10001031
return {
@@ -1092,6 +1123,9 @@ export const getFormattedTokens = (coinMap = coins): Tokens => {
10921123
cosmos: {
10931124
tokens: getFormattedCosmosChainTokens(coinMap).filter((token) => token.network === 'Mainnet'),
10941125
},
1126+
ton: {
1127+
tokens: getFormattedTonTokens(coinMap).filter((token) => token.network === 'Mainnet'),
1128+
},
10951129
},
10961130
testnet: {
10971131
eth: {
@@ -1187,6 +1221,9 @@ export const getFormattedTokens = (coinMap = coins): Tokens => {
11871221
cosmos: {
11881222
tokens: getFormattedCosmosChainTokens(coinMap).filter((token) => token.network === 'Testnet'),
11891223
},
1224+
ton: {
1225+
tokens: getFormattedTonTokens(coinMap).filter((token) => token.network === 'Testnet'),
1226+
},
11901227
},
11911228
};
11921229
};
@@ -1292,6 +1329,8 @@ export function getFormattedTokenConfigForCoin(coin: Readonly<BaseCoin>): TokenC
12921329
return getCosmosTokenConfig(coin);
12931330
} else if (coin instanceof VetToken) {
12941331
return getVetTokenConfig(coin);
1332+
} else if (coin instanceof TonToken) {
1333+
return getTonTokenConfig(coin);
12951334
}
12961335
return undefined;
12971336
}

0 commit comments

Comments
 (0)