1+ import { Address , WalletContractV4 } from '@ton/ton' ;
2+ import { TonConnectError } from './exceptions/TonConnectError' ;
13import { TonProvider } from './TonProvider' ;
24import {
35 ConnectItemReply ,
@@ -7,7 +9,21 @@ import {
79
810interface ITransaction {
911 valid_until : number ;
10- messages : { state_init : string ; address : string } [ ] ;
12+ messages : {
13+ stateInit : string ;
14+ state_init : string ;
15+ address : string ;
16+ amount : string ;
17+ } [ ] ;
18+ network : string ;
19+ from : string ;
20+ }
21+
22+ interface TransformedTransaction {
23+ messages : ITransaction [ 'messages' ] ;
24+ valid_until : number ;
25+ network : string ;
26+ from : string ;
1127}
1228
1329/**
@@ -19,21 +35,23 @@ interface ITransaction {
1935export class MobileAdapter {
2036 provider : TonProvider ;
2137
38+ private rawAddress : string | null = null ;
39+
2240 constructor ( provider : TonProvider ) {
2341 this . provider = provider ;
2442 }
2543
26- static mapToCamelCase ( transaction : ITransaction ) {
44+ static mapToCamelCase ( transaction : ITransaction ) : TransformedTransaction {
2745 return {
2846 ...transaction ,
2947 ...( transaction ?. messages
3048 ? {
31- messages : ( transaction ?. messages || [ ] ) . map (
32- ( { state_init , ...message } ) => ( {
33- ...message ,
34- stateInit : state_init ,
35- } ) ,
36- ) ,
49+ messages : ( transaction ?. messages || [ ] ) . map ( ( message ) => ( {
50+ ...message ,
51+ ...( 'state_init' in message || 'stateInit' in message
52+ ? { stateInit : message . state_init || message . stateInit }
53+ : { } ) ,
54+ } ) ) ,
3755 }
3856 : { } ) ,
3957 } ;
@@ -60,11 +78,16 @@ export class MobileAdapter {
6078 console . warn ( 'type parameter removed from request' ) ;
6179 }
6280
81+ this . rawAddress = rest . address ;
82+
6383 return rest ;
6484 }
6585
6686 if ( item . name === 'ton_proof' ) {
67- const response = item as TonProofItemReplySuccess ;
87+ const { type, ...response } = item as TonProofItemReplySuccess & {
88+ type ?: string ;
89+ } ;
90+
6891 return {
6992 ...response ,
7093 proof : {
@@ -83,17 +106,35 @@ export class MobileAdapter {
83106 'tonConnect_reconnect' ,
84107 params ,
85108 ) ;
86- return JSON . parse ( res ) ;
109+
110+ const parsedResponse = JSON . parse ( res ) ;
111+
112+ const { nonBounceable, type, ...rest } =
113+ parsedResponse [ 0 ] as TonAddressItemReply & {
114+ nonBounceable : string ;
115+ type ?: string ;
116+ } ;
117+
118+ this . rawAddress = rest . address ;
119+
120+ return [ rest ] as T ;
87121 }
88122
89123 case 'ton_rawSign' :
90124 return this . provider . internalRequest < T > ( 'signMessage' , params ) ;
91125
92126 case 'ton_sendTransaction' :
93127 case 'tonConnect_sendTransaction' : {
128+ const tx = ( params as object [ ] ) [ 0 ] as ITransaction ;
129+
130+ this . validateNetwork ( tx ) ;
131+ this . validateMessagesAddresses ( tx ) ;
132+ this . validateFromAddress ( tx ) ;
133+ this . validateTransaction ( MobileAdapter . mapToCamelCase ( tx ) ) ;
134+
94135 const res = await this . provider . internalRequest < string > (
95136 'signTransaction' ,
96- MobileAdapter . mapToCamelCase ( ( params as object [ ] ) [ 0 ] as ITransaction ) ,
137+ MobileAdapter . mapToCamelCase ( tx ) ,
97138 ) ;
98139
99140 const { nonce, hash } = JSON . parse ( res ) ;
@@ -132,4 +173,71 @@ export class MobileAdapter {
132173 return this . provider . internalRequest ( method , params ) ;
133174 }
134175 }
176+
177+ validateTransaction ( tx : TransformedTransaction ) {
178+ // throw error if there is a message with empty state init
179+ if (
180+ tx . messages . some (
181+ ( message ) => 'stateInit' in message && message . stateInit . length === 0 ,
182+ )
183+ ) {
184+ console . error ( 'Empty state init in message' ) ;
185+ throw new TonConnectError ( 'Bad request' , 1 ) ;
186+ }
187+
188+ // throw error if there is a message with amount not being a string
189+ if ( tx . messages . some ( ( message ) => typeof message . amount !== 'string' ) ) {
190+ console . error ( 'Invalid amount type' ) ;
191+ throw new TonConnectError ( 'Bad request' , 1 ) ;
192+ }
193+
194+ // throw error if valid until is not a number
195+ if ( typeof tx . valid_until !== 'number' ) {
196+ console . error ( 'Invalid valid_until type' ) ;
197+ throw new TonConnectError ( 'Bad request' , 1 ) ;
198+ }
199+ }
200+
201+ validateFromAddress ( tx : ITransaction ) {
202+ if ( ! this . rawAddress ) {
203+ console . error ( 'Trying to execute transaction with invalid address' ) ;
204+ throw new TonConnectError ( 'Bad request' , 1 ) ;
205+ }
206+
207+ const address = Address . parseRaw ( this . rawAddress ) ;
208+
209+ const collection = [
210+ address . toRawString ( ) ,
211+ address . toString ( { bounceable : true } ) ,
212+ address . toString ( { bounceable : false } ) ,
213+ ] ;
214+
215+ if ( ! collection . includes ( tx . from ) ) {
216+ console . error ( 'from field does not match any user address' ) ;
217+ throw new TonConnectError ( 'Bad request' , 1 ) ;
218+ }
219+ }
220+
221+ /**
222+ * Validation on messages
223+ * @param tx
224+ */
225+ validateMessagesAddresses ( tx : ITransaction ) {
226+ // Message addresses can not be raw
227+ if ( tx . messages . some ( ( e ) => e . address . includes ( ':' ) ) ) {
228+ console . error ( 'Bad request, message address is invalid' ) ;
229+ throw new TonConnectError ( 'Bad request' , 1 ) ;
230+ }
231+ }
232+
233+ /**
234+ * Enforce mainnet
235+ * @param tx
236+ */
237+ validateNetwork ( tx : ITransaction ) {
238+ if ( tx . network !== '-239' ) {
239+ console . error ( 'Bad request, network id is invalid' ) ;
240+ throw new TonConnectError ( 'Bad request' , 1 ) ;
241+ }
242+ }
135243}
0 commit comments