@@ -9,6 +9,32 @@ import {
99 useTonConnectUI ,
1010 useTonWallet
1111} from '@tonconnect/ui-react' ;
12+ import { Cell , loadMessage } from '@ton/core' ;
13+
14+ /**
15+ * Parse extension address from BOC (external message)
16+ * @param boc - Base64 encoded BOC string
17+ * @returns Extension address in user-friendly format or error message
18+ */
19+ function parseExtensionAddressFromBoc ( boc : string ) : string {
20+ try {
21+ const slice = Cell . fromBase64 ( boc ) . beginParse ( ) ;
22+ const message = loadMessage ( slice ) ;
23+
24+ // Extract destination address from message info
25+ if ( message . info . type === 'external-out' ) {
26+ return message . info . dest ?. toString ( ) || 'No destination address found' ;
27+ } else if ( message . info . type === 'internal' ) {
28+ return message . info . dest . toString ( ) ;
29+ } else if ( message . info . type === 'external-in' ) {
30+ return 'External-in message (no destination)' ;
31+ }
32+
33+ return 'Unknown message type' ;
34+ } catch ( error ) {
35+ return `Error parsing BOC: ${ error instanceof Error ? error . message : String ( error ) } ` ;
36+ }
37+ }
1238
1339const baseSubscriptionPayload : CreateSubscriptionV2Request = {
1440 validUntil : Math . floor ( Date . now ( ) / 1000 ) + 600 , // 10 minutes from now
@@ -32,23 +58,35 @@ const baseSubscriptionPayload: CreateSubscriptionV2Request = {
3258 }
3359} ;
3460
61+ const baseCancelPayload : CancelSubscriptionV2Request = {
62+ validUntil : Math . floor ( Date . now ( ) / 1000 ) + 600 , // 10 minutes from now
63+ extensionAddress : ''
64+ } ;
65+
3566export function SubscriptionForm ( ) {
3667 const [ subscription , setSubscription ] =
3768 useState < CreateSubscriptionV2Request > ( baseSubscriptionPayload ) ;
3869 const [ subscriptionRes , setSubscriptionRes ] = useState < CreateSubscriptionV2Response | null > (
3970 null
4071 ) ;
4172 const [ subscriptionError , setSubscriptionError ] = useState < string | null > ( null ) ;
73+
74+ const [ cancelPayload , setCancelPayload ] =
75+ useState < CancelSubscriptionV2Request > ( baseCancelPayload ) ;
4276 const [ cancelRes , setCancelRes ] = useState < CancelSubscriptionV2Response | null > ( null ) ;
4377 const [ cancelError , setCancelError ] = useState < string | null > ( null ) ;
4478
4579 const wallet = useTonWallet ( ) ;
4680 const [ tonConnectUi ] = useTonConnectUI ( ) ;
4781
48- const onChange = useCallback ( ( value : object ) => {
82+ const onSubscriptionChange = useCallback ( ( value : object ) => {
4983 setSubscription ( ( value as { updated_src : typeof subscription } ) . updated_src ) ;
5084 } , [ ] ) ;
5185
86+ const onCancelChange = useCallback ( ( value : object ) => {
87+ setCancelPayload ( ( value as { updated_src : typeof cancelPayload } ) . updated_src ) ;
88+ } , [ ] ) ;
89+
5290 // const loadTemplate = (template: CreateSubscriptionV2Request) => {
5391 // setSubscription(template);
5492 // };
@@ -60,6 +98,13 @@ export function SubscriptionForm() {
6098 . then ( res => {
6199 setSubscriptionRes ( res ) ;
62100 setSubscriptionError ( null ) ;
101+ // Auto-fill extensionAddress in cancel form
102+ if ( res . extensionAddress ) {
103+ setCancelPayload ( prev => ( {
104+ ...prev ,
105+ extensionAddress : res . extensionAddress as string
106+ } ) ) ;
107+ }
63108 } )
64109 . catch ( err => {
65110 setSubscriptionError ( err instanceof Error ? err . message : String ( err ) ) ;
@@ -68,21 +113,14 @@ export function SubscriptionForm() {
68113 } ;
69114
70115 const onCancel = ( ) => {
71- if ( ! subscriptionRes ?. boc ) {
72- console . error ( 'No subscription response boc available ' ) ;
116+ if ( ! cancelPayload . extensionAddress ) {
117+ setCancelError ( 'No extensionAddress provided ' ) ;
73118 return ;
74119 }
75120
76- const cancelRequest : CancelSubscriptionV2Request = {
77- validUntil : Math . floor ( Date . now ( ) / 1000 ) + 600 , // 10 minutes from now
78- extensionAddress : subscriptionRes . boc ,
79- network : subscription . network ,
80- from : subscription . from
81- } ;
82-
83121 setCancelError ( null ) ;
84122 tonConnectUi
85- . cancelSubscription ( cancelRequest , { version : 'v2' } )
123+ . cancelSubscription ( cancelPayload , { version : 'v2' } )
86124 . then ( res => {
87125 setCancelRes ( res ) ;
88126 setCancelError ( null ) ;
@@ -95,66 +133,100 @@ export function SubscriptionForm() {
95133
96134 return (
97135 < div className = "create-subscription-form" >
98- < h3 > Configure and create subsciption</ h3 >
99- < h4 > Subscription data </ h4 >
100-
101- { /* <div className="template-buttons">
102- <button onClick={() => loadTemplate(defaultTextData)}>
103- Text
104- </button>
105- <button onClick={() => loadTemplate(defaultBinaryData)}>
106- Binary
107- </button>
108- <button onClick={() => loadTemplate(defaultCellData)}>
109- Cell
110- </button>
111- </div> */ }
112- < ReactJson
113- name = { false }
114- src = { subscription }
115- theme = "ocean"
116- onEdit = { onChange }
117- onAdd = { onChange }
118- onDelete = { onChange }
119- />
120- { subscriptionError && (
121- < >
122- < h4 style = { { color : 'red' } } > Create subscription error</ h4 >
123- < div style = { { color : 'red' , padding : '10px' , border : '1px solid red' } } >
124- { subscriptionError }
125- </ div >
126- </ >
127- ) }
128- { subscriptionRes && (
129- < >
130- < h4 style = { { color : 'green' } } > Create subscription response</ h4 >
131- < ReactJson name = { false } src = { subscriptionRes } theme = "ocean" />
132- </ >
133- ) }
134- { cancelError && (
135- < >
136- < h4 style = { { color : 'red' } } > Cancel subscription error</ h4 >
137- < div style = { { color : 'red' , padding : '10px' , border : '1px solid red' } } >
138- { cancelError }
136+ < h3 > Subscriptions</ h3 >
137+
138+ { /* SUBSCRIBE BLOCK */ }
139+ < div className = "subscription-block" >
140+ < h4 style = { { marginBottom : '10px' } } > Subscription Request Data</ h4 >
141+
142+ < ReactJson
143+ name = { false }
144+ src = { subscription }
145+ theme = "ocean"
146+ onEdit = { onSubscriptionChange }
147+ onAdd = { onSubscriptionChange }
148+ onDelete = { onSubscriptionChange }
149+ />
150+
151+ { subscriptionError && (
152+ < >
153+ < h4 style = { { color : 'red' } } > Error</ h4 >
154+ < div style = { { color : 'red' , padding : '10px' , border : '1px solid red' } } >
155+ { subscriptionError }
156+ </ div >
157+ </ >
158+ ) }
159+
160+ { subscriptionRes && (
161+ < >
162+ < h4 style = { { color : 'green' } } > Create subscription response</ h4 >
163+ < ReactJson name = { false } src = { subscriptionRes } theme = "ocean" />
164+ < div
165+ style = { {
166+ marginTop : '10px' ,
167+ padding : '10px' ,
168+ backgroundColor : '#f0f0f0' ,
169+ borderRadius : '4px'
170+ } }
171+ >
172+ < strong > Parsed Extension Address from BOC:</ strong >
173+ < div
174+ style = { {
175+ fontFamily : 'monospace' ,
176+ marginTop : '5px' ,
177+ wordBreak : 'break-all'
178+ } }
179+ >
180+ { parseExtensionAddressFromBoc ( subscriptionRes . boc ) }
181+ </ div >
182+ </ div >
183+ </ >
184+ ) }
185+
186+ { wallet && (
187+ < div className = "buttons-container" >
188+ < button onClick = { onSend } > Create subscription</ button >
139189 </ div >
140- </ >
141- ) }
142- { cancelRes && (
143- < >
144- < h4 style = { { color : 'green' } } > Cancel subscription response</ h4 >
145- < ReactJson name = { false } src = { cancelRes } theme = "ocean" />
146- </ >
147- ) }
148- { wallet && (
149- < div className = "buttons-container" >
150- < button onClick = { onSend } > Create subscription</ button >
151- { subscriptionRes && (
152- < button onClick = { onCancel } disabled = { ! subscriptionRes ?. boc } >
190+ ) }
191+ </ div >
192+
193+ { /* UNSUBSCRIBE BLOCK */ }
194+ < div className = "cancel-block" >
195+ < h4 style = { { marginBottom : '10px' } } > Cancel Request Data</ h4 >
196+
197+ < ReactJson
198+ name = { false }
199+ src = { cancelPayload }
200+ theme = "ocean"
201+ onEdit = { onCancelChange }
202+ onAdd = { onCancelChange }
203+ onDelete = { onCancelChange }
204+ />
205+
206+ { cancelError && (
207+ < >
208+ < h4 style = { { color : 'red' } } > Error</ h4 >
209+ < div style = { { color : 'red' , padding : '10px' , border : '1px solid red' } } >
210+ { cancelError }
211+ </ div >
212+ </ >
213+ ) }
214+
215+ { cancelRes && (
216+ < >
217+ < h4 style = { { color : 'green' } } > Cancel subscription response</ h4 >
218+ < ReactJson name = { false } src = { cancelRes } theme = "ocean" />
219+ </ >
220+ ) }
221+
222+ { wallet && (
223+ < div className = "buttons-container" >
224+ < button onClick = { onCancel } disabled = { ! cancelPayload . extensionAddress } >
153225 Cancel subscription
154226 </ button >
155- ) }
156- </ div >
157- ) }
227+ </ div >
228+ ) }
229+ </ div >
158230 </ div >
159231 ) ;
160232}
0 commit comments