1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
use alloc::borrow::Cow;
use alloc::string::String;
use alloc::vec::Vec;
use ink::ChainExtensionInstance;
use pink_types::sgx::SgxQuote;
pub use http_request::{HttpRequest, HttpRequestError, HttpResponse};
pub use ink::primitives::AccountId;
pub use signing::SigType;
use crate::{Balance, EcdsaPublicKey, EcdsaSignature, Hash};
pub use pink_types::js::{JsCode, JsValue};
pub use pink_types::result::Result as PinkExtResult;
#[cfg(doc)]
use crate::{debug, error, http_get, http_post, http_put, info, warn};
mod http_request;
pub mod signing;
#[cfg(feature = "std")]
pub mod test;
#[derive(scale::Encode, scale::Decode, Debug)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct StorageQuotaExceeded;
mod sealed {
pub trait Sealed {}
}
pub trait CodableError: sealed::Sealed {
fn encode(&self) -> u32;
fn decode(code: u32) -> Option<Self>
where
Self: Sized;
}
macro_rules! impl_codable_error_for {
($t: path) => {
impl sealed::Sealed for $t {}
impl CodableError for $t {
fn encode(&self) -> u32 {
1
}
fn decode(code: u32) -> Option<Self> {
if code == 1 {
Some(Self)
} else {
None
}
}
}
impl From<ErrorCode> for $t {
fn from(value: ErrorCode) -> Self {
match <Self as CodableError>::decode(value.0) {
None => crate::panic!("chain extension: invalid output"),
Some(err) => err,
}
}
}
impl From<scale::Error> for $t {
fn from(_value: scale::Error) -> Self {
crate::panic!("chain_ext: failed to decocde output")
}
}
};
}
impl_codable_error_for!(StorageQuotaExceeded);
pub struct EncodeOutput<T>(pub T);
pub trait EncodeOutputFallbask {
fn encode(self) -> (u32, Vec<u8>);
}
impl<T: scale::Encode, E: CodableError> EncodeOutput<Result<T, E>> {
pub fn encode(self) -> (u32, Vec<u8>) {
match self.0 {
Ok(val) => (0, val.encode()),
Err(err) => (err.encode(), Vec::new()),
}
}
}
impl<T: scale::Encode> EncodeOutputFallbask for EncodeOutput<T> {
fn encode(self) -> (u32, Vec<u8>) {
(0, self.0.encode())
}
}
#[derive(scale::Encode, scale::Decode, Debug)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct ErrorCode(u32);
impl ink::env::chain_extension::FromStatusCode for ErrorCode {
fn from_status_code(status_code: u32) -> Result<(), Self> {
match status_code {
0 => Ok(()),
_ => Err(ErrorCode(status_code)),
}
}
}
pub type BatchHttpResult = Result<Vec<Result<HttpResponse, HttpRequestError>>, HttpRequestError>;
/// Extensions for the ink runtime defined by phat contract.
#[pink_extension_macro::chain_extension]
pub trait PinkExt {
type ErrorCode = ErrorCode;
/// Make a HTTP request.
///
/// # Arguments
///
/// * `request`: A `HttpRequest` struct containing all the details for the HTTP request.
///
/// # Returns
///
/// * `HttpResponse` - The response from the HTTP request which contains the status code, headers, and body.
///
/// # Example
///
/// ```ignore
/// let request = HttpRequest::new("https://httpbin.org/get", "GET", Defualt::default(), Defualt::default());
/// let response = pink::ext().http_request(request);
/// ```
///
/// There are also some shortcut macros for this function:
/// - [`http_get!`]
/// - [`http_post!`]
/// - [`http_put!`]
///
/// # Availability
/// any contract | query only
#[ink(extension = 1, handle_status = false)]
fn http_request(request: HttpRequest) -> HttpResponse;
/// Sign a message with a given key.
///
/// # Arguments
///
/// * `sigtype`: The signature type to use for signing the message.
/// * `key`: The private key used for signing the message.
/// * `message`: The message to be signed.
///
/// # Returns
///
/// * `Vec<u8>` - The signed message as a byte vector.
///
/// # Example
///
/// ```ignore
/// let derived_key = pink::ext().derive_sr25519_key(b"some salt".into());
/// let message = b"Hello, world!";
/// let signature = pink::ext().sign(SigType::Sr25519, &key, message);
/// let pubkey = pink::ext().get_public_key(SigType::Sr25519, &derived_key);
/// let is_valid = pink::ext().verify(SigType::Sr25519, &pubkey, message, &signature);
/// ```
///
/// # Availability
/// For SigType::Sr25519:
/// any contract | query only
///
/// For Others:
/// any contract | query | transaction
#[ink(extension = 2, handle_status = false)]
fn sign(sigtype: SigType, key: &[u8], message: &[u8]) -> Vec<u8>;
/// Verify a signature.
///
/// This method verifies a digital signature given the signature type, public key, message, and signature.
///
/// # Arguments
///
/// * `sigtype`: The type of signature to verify.
/// * `pubkey`: The public key associated with the private key that signed the message.
/// * `message`: The original message that was signed.
/// * `signature`: The digital signature to verify.
///
/// # Returns
///
/// * `bool` - `true` if the signature is valid, `false` otherwise.
///
/// # Example
///
/// ```ignore
/// let derived_key = pink::ext().derive_sr25519_key(b"some salt".into());
/// let message = b"Hello, world!";
/// let signature = pink::ext().sign(SigType::Sr25519, &key, message);
/// let pubkey = pink::ext().get_public_key(SigType::Sr25519, &derived_key);
/// let is_valid = pink::ext().verify(SigType::Sr25519, &pubkey, message, &signature);
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 3, handle_status = false)]
fn verify(sigtype: SigType, pubkey: &[u8], message: &[u8], signature: &[u8]) -> bool;
/// Derive a sr25519 key.
///
/// This method derives a sr25519 key using the provided salt and the contract private key.
/// The derived key is deterministic so it could be used in transactions to sign messages.
///
/// The derived key can also be used as a cryptographically secure entropy source.
///
/// # Arguments
///
/// * `salt`: The salt to use in the key derivation function.
///
/// # Returns
///
/// * `Vec<u8>` - The derived sr25519 key as a byte vector.
///
/// # Example
///
/// ```ignore
/// let derived_key = pink::ext().derive_sr25519_key(b"some salt".into());
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 4, handle_status = false)]
fn derive_sr25519_key(salt: Cow<[u8]>) -> Vec<u8>;
/// Derive the public key from a private key.
///
/// This method takes a signature type and private key and returns the associated public key.
///
/// # Arguments
///
/// * `sigtype`: The type of signature to generate the public key for.
/// * `key`: The private key used to generate the public key.
///
/// # Returns
///
/// * `Vec<u8>` - The public key associated with the given private key as a byte vector.
///
/// # Example
///
/// ```ignore
/// let derived_key = pink::ext().derive_sr25519_key(b"some salt".into());
/// let pubkey = pink::ext().get_public_key(SigType::Sr25519, &derived_key);
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 5, handle_status = false)]
fn get_public_key(sigtype: SigType, key: &[u8]) -> Vec<u8>;
/// Set a value in the local cache.
///
/// This method sets a value in the local cache with the default expiration time of 7 days.
/// To set a custom expiration time, use `cache_set_expiration`.
/// Values stored in cache can only be read in query functions.
/// Always returns `Ok(())` if it is called from a transaction context.
///
/// # Arguments
///
/// * `key`: The key used to identify the value in the cache.
/// * `value`: The value to be stored in the cache.
///
/// # Returns
///
/// * `Result<(), StorageQuotaExceeded>` - `Ok(())` or `Err(StorageQuotaExceeded)` if the storage quota is exceeded.
///
/// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
/// <strong>Warning:</strong>
/// The cache is not guaranteed to be persistent. It may be cleared at any time due
/// to various reasons:
///
/// - The cached item is expired.
/// - The entire cache in pRuntime is full and a new value needs to be stored (either from the contract itself or
/// other contracts).
/// - The worker is restarted.
/// </p>
///
/// In order to use cache, the contract need to be staked via the phala on-chain API `PhatTokenomic::adjust_stake`.
/// All contracts will share the 20MB cache storage by the ratio of stake.
///
/// # Example
///
/// ```ignore
/// let key = b"my key";
/// let value = b"my value";
/// let result = pink::ext().cache_set(key, value);
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 6, handle_status = true)]
fn cache_set(key: &[u8], value: &[u8]) -> Result<(), StorageQuotaExceeded>;
/// Set the expiration time of a value in the local cache.
///
/// This method sets the expiration time for a given key in the local cache.
///
/// # Arguments
///
/// * `key`: The key of the value to set the expiration time for.
/// * `expire`: The expiration time from now in seconds.
///
/// # Example
///
/// ```ignore
/// let key = b"my key";
/// let expire = 60; // 1 minute
/// pink::ext().cache_set_expiration(key, expire);
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 7, handle_status = false)]
fn cache_set_expiration(key: &[u8], expire: u64);
/// Get a value from the local cache.
///
/// This method retrieves a value from the local cache. It can only be used in query functions.
/// If called from a transaction context, it will always return `None`.
///
/// # Arguments
///
/// * `key`: The key used to identify the value in the cache.
///
/// # Returns
///
/// * `Option<Vec<u8>>` - The value from the cache as a byte vector wrapped in an Option,
/// or `None` if the value does not exist or called in transaction.
///
/// # Example
///
/// ```ignore
/// let key = b"my key";
/// let value = pink::ext().cache_get(key);
/// ```
///
/// # Availability
/// any contract | query
#[ink(extension = 8, handle_status = false)]
fn cache_get(key: &[u8]) -> Option<Vec<u8>>;
/// Remove a value from the local cache.
///
/// This method removes a value from the local cache and returns the removed value if it existed.
/// If called from a transaction context, it will always return `None`.
///
/// # Arguments
///
/// * `args`: The key used to identify the value in the cache.
///
/// # Returns
///
/// * `Option<Vec<u8>>` - The removed value as a byte vector wrapped in an Option
/// or `None` if the value did not exist or called in transaction.
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 9, handle_status = false)]
fn cache_remove(args: &[u8]) -> Option<Vec<u8>>;
/// Log a message.
///
/// This method logs a message at a given level.
///
/// The logs would be shown in the worker log file. Additionally, if a log server
/// contract has been deployed in the cluster, the logs would be sent to the log server.
/// Users can query the logs via the log server API.
///
/// # Arguments
///
/// * `level`: The level of the log message.
/// * `message`: The message to be logged.
///
/// # Example
///
/// ```ignore
/// let level = 1;
/// let message = "Hello, world!";
/// pink::ext().log(level, message);
/// ```
///
/// # Note
/// This is the low-level method for logging. It is recommended to use shortcuts macros below instead:
///
/// - [`debug!`]
/// - [`info!`]
/// - [`warn!`]
/// - [`error!`]
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 10, handle_status = false)]
fn log(level: u8, message: &str);
/// Get random bytes.
///
/// This method generates a vector of random bytes of a given length. It returns random bytes
/// generated by hardware RNG. So it is not deterministic and only available in a query context.
///
/// # Note
/// It always returns an empty vec![] when called in a transaction.
///
///
/// # Arguments
///
/// * `length`: The length of the random bytes vector.
///
/// # Returns
///
/// * `Vec<u8>` - A vector of random bytes of the given length.
///
/// # Example
///
/// ```ignore
/// let length = 32;
/// let random_bytes = pink::ext().getrandom(length);
/// ```
///
/// # Availability
/// any contract | query only
#[ink(extension = 11, handle_status = false)]
fn getrandom(length: u8) -> Vec<u8>;
/// Check if it is running in a transaction context.
///
/// # Returns
///
/// * `bool` - `true` if it is running in a transaction context, `false` if in query.
///
/// # Availability
/// any contract | query | transaction
#[allow(clippy::wrong_self_convention)]
#[ink(extension = 12, handle_status = false)]
fn is_in_transaction() -> bool;
/// Sign a prehashed message with a given key.
///
/// This method uses the given key and prehashed message to create a ECDSA signature.
///
/// # Arguments
///
/// * `key`: The private key used for signing the message.
/// * `message_hash`: The prehashed message to be signed.
///
/// # Returns
///
/// * `EcdsaSignature` - The signature of the message.
///
/// # Example
///
/// ```ignore
/// let key = [0u8; 32]; // replace with actual key
/// let message_hash = Hash::zero(); // replace with actual hash
/// let signature = pink::ext().ecdsa_sign_prehashed(&key, message_hash);
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 13, handle_status = false)]
fn ecdsa_sign_prehashed(key: &[u8], message_hash: Hash) -> EcdsaSignature;
/// Verify a prehashed ECDSA signature.
///
/// This method verifies a prehashed ECDSA signature given the signature, prehashed message, and public key.
///
/// # Arguments
///
/// * `signature`: The ECDSA digital signature to verify.
/// * `message_hash`: The prehashed original message that was signed.
/// * `pubkey`: The public key associated with the private key that signed the message.
///
/// # Returns
///
/// * `bool` - `true` if the signature is valid, `false` otherwise.
///
/// # Example
///
/// ```ignore
/// let signature = EcdsaSignature::default(); // replace with actual signature
/// let message_hash = Hash::zero(); // replace with actual hash
/// let pubkey = EcdsaPublicKey::default(); // replace with actual pubkey
/// let is_valid = pink::ext().ecdsa_verify_prehashed(signature, message_hash, pubkey);
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 14, handle_status = false)]
fn ecdsa_verify_prehashed(
signature: EcdsaSignature,
message_hash: Hash,
pubkey: EcdsaPublicKey,
) -> bool;
/// Get the contract id of the preinstalled system contract.
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 15, handle_status = false)]
fn system_contract_id() -> AccountId;
/// Get balance of a given address.
///
/// # Arguments
///
/// * `account`: The `AccountId` of the contract.
///
/// # Returns
///
/// * `(Balance, Balance)` - The total and free balance of a given contract.
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 16, handle_status = false)]
fn balance_of(account: AccountId) -> (Balance, Balance);
/// Get the public key of the worker running this query.
///
/// # Returns
///
/// * `crate::EcdhPublicKey` - The public key of the worker.
///
/// # Availability
/// any contract | query only
#[ink(extension = 17, handle_status = false)]
fn worker_pubkey() -> crate::EcdhPublicKey;
/// Get current millis since Unix epoch.
///
/// This method returns the current time as milliseconds since the Unix epoch from the OS.
///
/// # Returns
///
/// * `u64` - The current time as milliseconds since the Unix epoch from the OS.
///
/// # Note
/// Because this method uses the OS time, it is not deterministic and may be manipulated by compromised OS.
///
/// # Example
///
/// ```ignore
/// let current_millis = pink::ext().untrusted_millis_since_unix_epoch();
/// ```
///
/// # Availability
/// any contract | query only
#[ink(extension = 18, handle_status = false)]
fn untrusted_millis_since_unix_epoch() -> u64;
/// Check whether the code exists in the cluster storage.
///
/// # Returns
///
/// * `bool` - `true` if the code exists, `false` otherwise.
///
/// # Example
///
/// ```ignore
/// let code_hash = Hash::zero(); // replace with actual code hash
/// let exists = pink::ext().code_exists(code_hash, false);
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 19, handle_status = false)]
fn code_exists(code_hash: Hash, sidevm: bool) -> bool;
/// Import the latest system contract code from chain storage to the cluster storage.
///
/// # Returns
///
/// * `Option<Hash>` - The code hash of the latest system contract code, or `None` if the import failed.
///
/// # Example
///
/// ```ignore
/// let payer = AccountId::default(); // replace with actual payer id
/// let code_hash = pink::ext().import_latest_system_code(payer);
/// ```
///
/// # Availability
/// system only | query | transaction
#[ink(extension = 20, handle_status = false)]
fn import_latest_system_code(payer: AccountId) -> Option<Hash>;
/// Get the version of the current contract runtime.
///
/// # Returns
///
/// * `(u32, u32)` - The version of the current contract runtime in this cluster as a tuple (major, minor).
///
/// # Example
///
/// ```ignore
/// let (major, minor) = pink::ext().runtime_version();
/// ```
///
/// # Availability
/// any contract | query | transaction
#[ink(extension = 21, handle_status = false)]
fn runtime_version() -> (u32, u32);
/// Batch HTTP request.
///
/// This method sends a batch of HTTP requests with a given timeout and returns the results.
///
/// # Arguments
///
/// * `requests`: A vector of `HttpRequest` structs containing all the details for the HTTP requests.
/// * `timeout_ms`: The timeout for the batch request in milliseconds.
///
/// # Returns
///
/// * `BatchHttpResult` - A vector of response to eahch HTTP requests.
///
/// # Example
///
/// ```ignore
/// let requests = vec![
/// HttpRequest::new("https://httpbin.org/get",
/// "GET",
/// Default::default(),
/// Default::default(),
/// ),
/// HttpRequest::new("https://httpbin.org/post",
/// "POST",
/// Default::default(),
/// b"Hello, world!".to_vec(),
/// ),
/// ];
/// let result = pink::ext().batch_http_request(requests, 5000);
///
/// # Runtime version
/// 1.1
/// ```
#[ink(extension = 22, handle_status = true)]
fn batch_http_request(requests: Vec<HttpRequest>, timeout_ms: u64) -> BatchHttpResult;
/// Get current event chain head info
///
/// Returns (next event block number, last event block hash)
///
/// # Runtime version
/// 1.1
#[ink(extension = 23, handle_status = false)]
fn current_event_chain_head() -> (u64, Hash);
/// Execute JavaScript code using SideVM through the JsRuntime driver.
///
/// # Arguments
///
/// * `codes`: Vector of `JsCode` representing JavaScript code to be evaluated.
/// * `args`: Vector of `String`, passed into the script as `scriptArgs`.
///
/// # Returns
///
/// * `JsValue`: Result of the JavaScript expression evaluation.
///
/// # Notes
/// The underlying JavaScript engine is an extended version of QuickJS, compiled to WebAssembly
/// and running inside SideVM. This environment has several limitations compared to more
/// traditional JavaScript engines like those in browsers or Node.js. For details on JavaScript
/// language features supported, refer to the [official QuickJS documentation](https://bellard.org/quickjs/).
///
/// In terms of library features, some basic asynchronous I/O APIs are added to QuickJS, including:
/// - [setTimeout/setInterval](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout)
/// - [Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API)
/// - [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL)
/// - [TextEncoder/Decoder](https://developer.mozilla.org/zh-CN/docs/Web/API/TextEncoder) -
/// Note that this implementation is incomplete. It only supports utf8 encoding/decoding.
/// Additional polyfills may be necessary for other requirements.
/// - [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
/// - [XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest)
///
/// # Returning Values from JavaScript to Rust
/// There are two methods to return values from JavaScript to Rust:
/// - Last Expression Value: By default, the value of the last JavaScript code
/// expression is returned to Rust. However, this can be problematic in asynchronous code.
/// - `globalThis.scriptOutput`: If a value is assigned to `globalThis.scriptOutput` within the
/// script, it will be returned to Rust, taking precedence over the last expression's value.
///
/// # Examples
///
/// ```ignore
/// // Return value via the last expression.
/// let js_code = "console.log('Hello, world!'); scriptArgs[0];"
/// let result = pink::ext().js_eval(vec![js_code.into()], vec!["bar".into()]);
/// assert_eq!(result, Ok(JsValue::String("bar".to_string())));
///
/// // Return value by setting scriptOutput.
/// let js_code = r#"
/// setTimeout(function() {
/// scriptOutput = scriptArgs[0];
/// }, 1000);
/// "#;
/// let result = pink::ext().js_eval(vec![js_code.into()], vec!["bar".into()]);
/// assert_eq!(result, Ok(JsValue::String("bar".to_string())));
/// ```
///
/// # Runtime version
/// 1.2
#[ink(extension = 24, handle_status = false)]
fn js_eval(codes: Vec<JsCode>, args: Vec<String>) -> JsValue;
/// Get the SGX quote of the worker running this query.
///
/// # Availability
/// any contract | query only
///
/// # Runtime version
/// 1.2
///
/// # Example
///
/// ```ignore
/// use pink::types::sgx::SgxQuote;
/// let SgxQuote { attestation_type, quote } = pink::ext().worker_sgx_quote().unwrap();
/// ```
#[ink(extension = 25, handle_status = false)]
fn worker_sgx_quote() -> Option<SgxQuote>;
}
pub fn pink_extension_instance() -> <PinkExt as ChainExtensionInstance>::Instance {
<PinkExt as ChainExtensionInstance>::instantiate()
}