testnumbat_wasm_node/api/
blockchain_api_node.rs

1use crate::api::managed_types::managed_buffer_api_node::unsafe_buffer_load_address;
2use testnumbat_wasm::{
3    api::BlockchainApi,
4    types::{
5        Address, BigUint, Box, DcdtTokenData, DcdtTokenType, ManagedAddress, ManagedBuffer,
6        ManagedType, ManagedVec, TokenIdentifier, H256,
7    },
8};
9
10#[allow(unused)]
11extern "C" {
12    // managed buffer API
13    fn mBufferNew() -> i32;
14
15    // address utils
16    fn getSCAddress(resultOffset: *mut u8);
17    #[cfg(not(feature = "unmanaged-ei"))]
18    fn managedSCAddress(resultHandle: i32);
19
20    fn getOwnerAddress(resultOffset: *mut u8);
21    #[cfg(not(feature = "unmanaged-ei"))]
22    fn managedOwnerAddress(resultHandle: i32);
23
24    fn getCaller(resultOffset: *mut u8);
25    #[cfg(not(feature = "unmanaged-ei"))]
26    fn managedCaller(resultHandle: i32);
27
28    fn getShardOfAddress(address_ptr: *const u8) -> i32;
29    fn isSmartContract(address_ptr: *const u8) -> i32;
30
31    /// Currently not used.
32    #[allow(dead_code)]
33    fn blockHash(nonce: i64, resultOffset: *mut u8) -> i32;
34
35    /// Currently not used.
36    #[allow(dead_code)]
37    fn getFunction(functionOffset: *const u8) -> i32;
38
39    fn getGasLeft() -> i64;
40    fn getBlockTimestamp() -> i64;
41    fn getBlockNonce() -> i64;
42    fn getBlockRound() -> i64;
43    fn getBlockEpoch() -> i64;
44    fn getBlockRandomSeed(resultOffset: *mut u8);
45    /// Currently not used.
46    #[allow(dead_code)]
47    fn getStateRootHash(resultOffset: *mut u8);
48    fn getPrevBlockTimestamp() -> i64;
49    fn getPrevBlockNonce() -> i64;
50    fn getPrevBlockRound() -> i64;
51    fn getPrevBlockEpoch() -> i64;
52    fn getPrevBlockRandomSeed(resultOffset: *const u8);
53    fn getOriginalTxHash(resultOffset: *const u8);
54
55    // Managed versions of the above
56    #[cfg(not(feature = "unmanaged-ei"))]
57    fn managedGetPrevBlockRandomSeed(resultHandle: i32);
58    #[cfg(not(feature = "unmanaged-ei"))]
59    fn managedGetBlockRandomSeed(resultHandle: i32);
60    #[cfg(not(feature = "unmanaged-ei"))]
61    fn managedGetStateRootHash(resultHandle: i32);
62    #[cfg(not(feature = "unmanaged-ei"))]
63    fn managedGetOriginalTxHash(resultHandle: i32);
64
65    // big int API
66    fn bigIntNew(value: i64) -> i32;
67    fn bigIntGetExternalBalance(address_ptr: *const u8, dest: i32);
68    fn bigIntGetDCDTExternalBalance(
69        address_ptr: *const u8,
70        tokenIDOffset: *const u8,
71        tokenIDLen: i32,
72        nonce: i64,
73        dest: i32,
74    );
75
76    // DCDT NFT
77    fn getCurrentDCDTNFTNonce(
78        address_ptr: *const u8,
79        tokenIDOffset: *const u8,
80        tokenIDLen: i32,
81    ) -> i64;
82    fn getDCDTTokenData(
83        address_ptr: *const u8,
84        tokenIDOffset: *const u8,
85        tokenIDLen: i32,
86        nonce: i64,
87        valueOffset: i32,
88        propertiesOffset: *const u8,
89        hashOffset: *const u8,
90        nameOffset: *const u8,
91        attributesOffset: *const u8,
92        creatorOffset: *const u8,
93        royaltiesOffset: i32,
94        urisOffset: *const u8,
95    ) -> i32;
96
97    // helper functions for getDCDTTokenData
98    fn getDCDTNFTNameLength(
99        address_ptr: *const u8,
100        tokenIDOffset: *const u8,
101        tokenIDLen: i32,
102        nonce: i64,
103    ) -> i32;
104    fn getDCDTNFTAttributeLength(
105        address_ptr: *const u8,
106        tokenIDOffset: *const u8,
107        tokenIDLen: i32,
108        nonce: i64,
109    ) -> i32;
110    fn getDCDTNFTURILength(
111        address_ptr: *const u8,
112        tokenIDOffset: *const u8,
113        tokenIDLen: i32,
114        nonce: i64,
115    ) -> i32;
116
117    #[cfg(not(feature = "unmanaged-ei"))]
118    fn managedGetDCDTTokenData(
119        addressHandle: i32,
120        tokenIDHandle: i32,
121        nonce: i64,
122        valueHandle: i32,
123        propertiesHandle: i32,
124        hashHandle: i32,
125        nameHandle: i32,
126        attributesHandle: i32,
127        creatorHandle: i32,
128        royaltiesHandle: i32,
129        urisHandle: i32,
130    );
131}
132
133impl BlockchainApi for crate::AndesApiImpl {
134    #[inline]
135    fn get_sc_address_legacy(&self) -> Address {
136        unsafe {
137            let mut res = Address::zero();
138            getSCAddress(res.as_mut_ptr());
139            res
140        }
141    }
142
143    #[inline]
144    #[cfg(not(feature = "unmanaged-ei"))]
145    fn get_sc_address(&self) -> ManagedAddress<Self> {
146        unsafe {
147            let handle = mBufferNew();
148            managedSCAddress(handle);
149            ManagedAddress::from_raw_handle(self.clone(), handle)
150        }
151    }
152
153    #[inline]
154    fn get_owner_address_legacy(&self) -> Address {
155        unsafe {
156            let mut res = Address::zero();
157            getOwnerAddress(res.as_mut_ptr());
158            res
159        }
160    }
161
162    #[inline]
163    #[cfg(not(feature = "unmanaged-ei"))]
164    fn get_owner_address(&self) -> ManagedAddress<Self> {
165        unsafe {
166            let handle = mBufferNew();
167            managedOwnerAddress(handle);
168            ManagedAddress::from_raw_handle(self.clone(), handle)
169        }
170    }
171
172    #[inline]
173    fn get_shard_of_address_legacy(&self, address: &Address) -> u32 {
174        unsafe { getShardOfAddress(address.as_ref().as_ptr()) as u32 }
175    }
176
177    #[inline]
178    fn get_shard_of_address(&self, address: &ManagedAddress<Self>) -> u32 {
179        unsafe { getShardOfAddress(unsafe_buffer_load_address(address.get_raw_handle())) as u32 }
180    }
181
182    #[inline]
183    fn is_smart_contract_legacy(&self, address: &Address) -> bool {
184        unsafe { isSmartContract(address.as_ref().as_ptr()) > 0 }
185    }
186
187    #[inline]
188    fn is_smart_contract(&self, address: &ManagedAddress<Self>) -> bool {
189        unsafe { isSmartContract(unsafe_buffer_load_address(address.get_raw_handle())) > 0 }
190    }
191
192    #[inline]
193    fn get_caller_legacy(&self) -> Address {
194        unsafe {
195            let mut res = Address::zero();
196            getCaller(res.as_mut_ptr());
197            res
198        }
199    }
200
201    #[inline]
202    #[cfg(not(feature = "unmanaged-ei"))]
203    fn get_caller(&self) -> ManagedAddress<Self> {
204        unsafe {
205            let handle = mBufferNew();
206            managedCaller(handle);
207            ManagedAddress::from_raw_handle(self.clone(), handle)
208        }
209    }
210
211    fn get_balance_legacy(&self, address: &Address) -> BigUint<Self> {
212        unsafe {
213            let balance_handle = bigIntNew(0);
214            bigIntGetExternalBalance(address.as_ref().as_ptr(), balance_handle);
215            BigUint::from_raw_handle(self.clone(), balance_handle)
216        }
217    }
218
219    fn get_balance(&self, address: &ManagedAddress<Self>) -> BigUint<Self> {
220        unsafe {
221            let balance_handle = bigIntNew(0);
222            bigIntGetExternalBalance(
223                unsafe_buffer_load_address(address.get_raw_handle()),
224                balance_handle,
225            );
226            BigUint::from_raw_handle(self.clone(), balance_handle)
227        }
228    }
229
230    #[inline]
231    fn get_state_root_hash_legacy(&self) -> H256 {
232        unsafe {
233            let mut res = H256::zero();
234            getOriginalTxHash(res.as_mut_ptr());
235            res
236        }
237    }
238
239    #[inline]
240    #[cfg(not(feature = "unmanaged-ei"))]
241    fn get_state_root_hash(&self) -> testnumbat_wasm::types::ManagedByteArray<Self, 32> {
242        unsafe {
243            let result_handle = mBufferNew();
244            managedGetStateRootHash(result_handle);
245            testnumbat_wasm::types::ManagedByteArray::from_raw_handle(self.clone(), result_handle)
246        }
247    }
248
249    #[inline]
250    fn get_tx_hash_legacy(&self) -> H256 {
251        unsafe {
252            let mut res = H256::zero();
253            getOriginalTxHash(res.as_mut_ptr());
254            res
255        }
256    }
257
258    #[inline]
259    #[cfg(not(feature = "unmanaged-ei"))]
260    fn get_tx_hash(&self) -> testnumbat_wasm::types::ManagedByteArray<Self, 32> {
261        unsafe {
262            let result_handle = mBufferNew();
263            managedGetOriginalTxHash(result_handle);
264            testnumbat_wasm::types::ManagedByteArray::from_raw_handle(self.clone(), result_handle)
265        }
266    }
267
268    #[inline]
269    fn get_gas_left(&self) -> u64 {
270        unsafe { getGasLeft() as u64 }
271    }
272
273    #[inline]
274    fn get_block_timestamp(&self) -> u64 {
275        unsafe { getBlockTimestamp() as u64 }
276    }
277
278    #[inline]
279    fn get_block_nonce(&self) -> u64 {
280        unsafe { getBlockNonce() as u64 }
281    }
282
283    #[inline]
284    fn get_block_round(&self) -> u64 {
285        unsafe { getBlockRound() as u64 }
286    }
287
288    #[inline]
289    fn get_block_epoch(&self) -> u64 {
290        unsafe { getBlockEpoch() as u64 }
291    }
292
293    #[inline]
294    fn get_block_random_seed_legacy(&self) -> Box<[u8; 48]> {
295        unsafe {
296            let mut res = [0u8; 48];
297            getBlockRandomSeed(res.as_mut_ptr());
298            Box::new(res)
299        }
300    }
301
302    #[inline]
303    #[cfg(not(feature = "unmanaged-ei"))]
304    fn get_block_random_seed(&self) -> testnumbat_wasm::types::ManagedByteArray<Self, 48> {
305        unsafe {
306            let result_handle = mBufferNew();
307            managedGetBlockRandomSeed(result_handle);
308            testnumbat_wasm::types::ManagedByteArray::from_raw_handle(self.clone(), result_handle)
309        }
310    }
311
312    #[inline]
313    fn get_prev_block_timestamp(&self) -> u64 {
314        unsafe { getPrevBlockTimestamp() as u64 }
315    }
316
317    #[inline]
318    fn get_prev_block_nonce(&self) -> u64 {
319        unsafe { getPrevBlockNonce() as u64 }
320    }
321
322    #[inline]
323    fn get_prev_block_round(&self) -> u64 {
324        unsafe { getPrevBlockRound() as u64 }
325    }
326
327    #[inline]
328    fn get_prev_block_epoch(&self) -> u64 {
329        unsafe { getPrevBlockEpoch() as u64 }
330    }
331
332    #[inline]
333    fn get_prev_block_random_seed_legacy(&self) -> Box<[u8; 48]> {
334        unsafe {
335            let mut res = [0u8; 48];
336            getPrevBlockRandomSeed(res.as_mut_ptr());
337            Box::new(res)
338        }
339    }
340
341    #[inline]
342    #[cfg(not(feature = "unmanaged-ei"))]
343    fn get_prev_block_random_seed(&self) -> testnumbat_wasm::types::ManagedByteArray<Self, 48> {
344        unsafe {
345            let result_handle = mBufferNew();
346            managedGetPrevBlockRandomSeed(result_handle);
347            testnumbat_wasm::types::ManagedByteArray::from_raw_handle(self.clone(), result_handle)
348        }
349    }
350
351    #[inline]
352    fn get_current_dcdt_nft_nonce(&self, address: &Address, token: &TokenIdentifier<Self>) -> u64 {
353        unsafe {
354            getCurrentDCDTNFTNonce(
355                address.as_ref().as_ptr(),
356                token.to_dcdt_identifier().as_ptr(),
357                token.len() as i32,
358            ) as u64
359        }
360    }
361
362    #[inline]
363    fn get_dcdt_balance(
364        &self,
365        m_address: &ManagedAddress<Self>,
366        token: &TokenIdentifier<Self>,
367        nonce: u64,
368    ) -> BigUint<Self> {
369        let address = m_address.to_address();
370        unsafe {
371            let balance_handle = bigIntNew(0);
372            bigIntGetDCDTExternalBalance(
373                address.as_ref().as_ptr(),
374                token.to_dcdt_identifier().as_ptr(),
375                token.len() as i32,
376                nonce as i64,
377                balance_handle,
378            );
379
380            BigUint::from_raw_handle(self.clone(), balance_handle)
381        }
382    }
383
384    #[cfg(feature = "unmanaged-ei")]
385    fn get_dcdt_token_data(
386        &self,
387        m_address: &ManagedAddress<Self>,
388        token: &TokenIdentifier<Self>,
389        nonce: u64,
390    ) -> DcdtTokenData<Self> {
391        use testnumbat_wasm::types::BoxedBytes;
392        let address = m_address.to_address();
393        unsafe {
394            let value_handle = bigIntNew(0);
395            let mut properties = [0u8; 2]; // always 2 bytes
396            let mut hash = BoxedBytes::allocate(128);
397
398            let name_len = getDCDTNFTNameLength(
399                address.as_ref().as_ptr(),
400                token.to_dcdt_identifier().as_ptr(),
401                token.len() as i32,
402                nonce as i64,
403            ) as usize;
404            let mut name_bytes = BoxedBytes::allocate(name_len);
405
406            let attr_len = getDCDTNFTAttributeLength(
407                address.as_ref().as_ptr(),
408                token.to_dcdt_identifier().as_ptr(),
409                token.len() as i32,
410                nonce as i64,
411            ) as usize;
412            let mut attr_bytes = BoxedBytes::allocate(attr_len);
413
414            // Current implementation of the underlying API only provides one URI
415            // In the future, this might be extended to multiple URIs per token,
416            // Hence the DcdtTokenData receives a Vec<BoxedBytes>
417            let uris_len = getDCDTNFTURILength(
418                address.as_ref().as_ptr(),
419                token.to_dcdt_identifier().as_ptr(),
420                token.len() as i32,
421                nonce as i64,
422            ) as usize;
423            let mut uri_bytes = BoxedBytes::allocate(uris_len);
424
425            let mut creator = Address::zero();
426            let royalties_handle = bigIntNew(0);
427
428            getDCDTTokenData(
429                address.as_ref().as_ptr(),
430                token.to_dcdt_identifier().as_ptr(),
431                token.len() as i32,
432                nonce as i64,
433                value_handle,
434                properties.as_mut_ptr(),
435                hash.as_mut_ptr(),
436                name_bytes.as_mut_ptr(),
437                attr_bytes.as_mut_ptr(),
438                creator.as_mut_ptr(),
439                royalties_handle,
440                uri_bytes.as_mut_ptr(),
441            );
442
443            // Fungible always have a nonce of 0, so we check nonce to figure out the type
444            let nonce = getCurrentDCDTNFTNonce(
445                address.as_ref().as_ptr(),
446                token.to_dcdt_identifier().as_ptr(),
447                token.len() as i32,
448            );
449            let token_type = if nonce == 0 {
450                DcdtTokenType::Fungible
451            } else {
452                DcdtTokenType::NonFungible
453            };
454
455            // Token is frozen if properties are not 0
456            let frozen = properties[0] == 0 && properties[1] == 0;
457
458            let mut uris_vec = ManagedVec::new(self.clone());
459            uris_vec.push(ManagedBuffer::new_from_bytes(
460                self.clone(),
461                uri_bytes.as_slice(),
462            ));
463
464            DcdtTokenData {
465                token_type,
466                amount: BigUint::from_raw_handle(self.clone(), value_handle),
467                frozen,
468                hash: ManagedBuffer::new_from_bytes(self.clone(), hash.as_slice()),
469                name: ManagedBuffer::new_from_bytes(self.clone(), name_bytes.as_slice()),
470                attributes: ManagedBuffer::new_from_bytes(self.clone(), attr_bytes.as_slice()),
471                creator: ManagedAddress::from_address(self.clone(), &creator),
472                royalties: BigUint::from_raw_handle(self.clone(), royalties_handle),
473                uris: uris_vec,
474            }
475        }
476    }
477
478    #[cfg(not(feature = "unmanaged-ei"))]
479    fn get_dcdt_token_data(
480        &self,
481        address: &ManagedAddress<Self>,
482        token: &TokenIdentifier<Self>,
483        nonce: u64,
484    ) -> DcdtTokenData<Self> {
485        let managed_token_id = token.as_managed_buffer();
486        unsafe {
487            let value_handle = bigIntNew(0);
488            let properties_handle = mBufferNew();
489            let hash_handle = mBufferNew();
490            let name_handle = mBufferNew();
491            let attributes_handle = mBufferNew();
492            let creator_handle = mBufferNew();
493            let royalties_handle = bigIntNew(0);
494            let uris_handle = mBufferNew();
495
496            managedGetDCDTTokenData(
497                address.get_raw_handle(),
498                managed_token_id.get_raw_handle(),
499                nonce as i64,
500                value_handle,
501                properties_handle,
502                hash_handle,
503                name_handle,
504                attributes_handle,
505                creator_handle,
506                royalties_handle,
507                uris_handle,
508            );
509
510            let token_type = if nonce == 0 {
511                DcdtTokenType::Fungible
512            } else {
513                DcdtTokenType::NonFungible
514            };
515
516            // here we trust Andes that it always gives us a properties buffer of length 2
517            let properties_buffer = ManagedBuffer::from_raw_handle(self.clone(), properties_handle);
518            let mut properties_bytes = [0u8; 2];
519            let _ = properties_buffer.load_slice(0, &mut properties_bytes[..]);
520            let frozen = properties_bytes[0] == 0 && properties_bytes[1] == 0; // token is frozen if properties are not 0
521
522            DcdtTokenData {
523                token_type,
524                amount: BigUint::from_raw_handle(self.clone(), value_handle),
525                frozen,
526                hash: ManagedBuffer::from_raw_handle(self.clone(), hash_handle),
527                name: ManagedBuffer::from_raw_handle(self.clone(), name_handle),
528                attributes: ManagedBuffer::from_raw_handle(self.clone(), attributes_handle),
529                creator: ManagedAddress::from_raw_handle(self.clone(), creator_handle),
530                royalties: BigUint::from_raw_handle(self.clone(), royalties_handle),
531                uris: ManagedVec::from_raw_handle(self.clone(), uris_handle),
532            }
533        }
534    }
535}