dusk_core/
transfer.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7//! Types related to Dusk's transfer contract that are shared across the
8//! network.
9
10#[cfg(feature = "serde")]
11use serde_with::{hex::Hex, serde_as, DisplayFromStr};
12
13use alloc::string::String;
14use alloc::vec::Vec;
15use core::cmp::max;
16use core::fmt::Debug;
17
18use bytecheck::CheckBytes;
19use dusk_bytes::{DeserializableSlice, Error as BytesError};
20use poseidon_merkle::Opening;
21use rand::{CryptoRng, RngCore};
22use rkyv::{Archive, Deserialize, Serialize};
23
24use crate::abi::ContractId;
25use crate::error::TxPreconditionError;
26use crate::signatures::bls::{
27    PublicKey as AccountPublicKey, SecretKey as AccountSecretKey,
28};
29use crate::{BlsScalar, Error};
30
31use self::data::{
32    BlobData, BlobSidecar, ContractCall, ContractDeploy, TransactionData,
33};
34use self::moonlight::Transaction as MoonlightTransaction;
35use self::phoenix::{
36    Note, Prove, PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey,
37    Sender, StealthAddress, Transaction as PhoenixTransaction,
38    NOTES_TREE_DEPTH,
39};
40use self::withdraw::{Withdraw, WithdrawReceiver};
41
42pub mod data;
43pub mod moonlight;
44pub mod phoenix;
45pub mod withdraw;
46
47/// ID of the genesis transfer contract
48pub const TRANSFER_CONTRACT: ContractId = crate::reserved(0x1);
49
50/// Panic of "Nonce not ready to be used yet"
51pub const PANIC_NONCE_NOT_READY: &str = "Nonce not ready to be used yet";
52
53/// Topic for the moonlight transaction event.
54pub const MOONLIGHT_TOPIC: &str = "moonlight";
55/// Topic for the phoenix transaction event.
56pub const PHOENIX_TOPIC: &str = "phoenix";
57/// Topic for the contract to contract transaction event.
58pub const CONTRACT_TO_CONTRACT_TOPIC: &str = "contract_to_contract";
59/// Topic for the contract to account transaction event.
60pub const CONTRACT_TO_ACCOUNT_TOPIC: &str = "contract_to_account";
61/// Topic for the withdraw event.
62pub const WITHDRAW_TOPIC: &str = "withdraw";
63/// Topic for the deposit event.
64pub const DEPOSIT_TOPIC: &str = "deposit";
65/// Topic for the convert event.
66pub const CONVERT_TOPIC: &str = "convert";
67/// Topic for the mint event.
68pub const MINT_TOPIC: &str = "mint";
69/// Topic for the mint to contract event.
70pub const MINT_CONTRACT_TOPIC: &str = "mint_c";
71
72/// The transaction used by the transfer contract.
73#[derive(Debug, Clone, Archive, PartialEq, Eq, Serialize, Deserialize)]
74#[archive_attr(derive(CheckBytes))]
75#[allow(clippy::large_enum_variant)]
76pub enum Transaction {
77    /// A phoenix transaction.
78    Phoenix(PhoenixTransaction),
79    /// A moonlight transaction.
80    Moonlight(MoonlightTransaction),
81}
82
83impl Transaction {
84    /// Create a new phoenix transaction.
85    ///
86    /// # Errors
87    /// The creation of a transaction is not possible and will error if:
88    /// - one of the input-notes doesn't belong to the `sender_sk`
89    /// - the transaction input doesn't cover the transaction costs
90    /// - the `inputs` vector is either empty or larger than 4 elements
91    /// - the `inputs` vector contains duplicate `Note`s
92    /// - the `Prove` trait is implemented incorrectly
93    #[allow(clippy::too_many_arguments)]
94    pub fn phoenix<R: RngCore + CryptoRng, P: Prove>(
95        rng: &mut R,
96        sender_sk: &PhoenixSecretKey,
97        refund_pk: &PhoenixPublicKey,
98        receiver_pk: &PhoenixPublicKey,
99        inputs: Vec<(Note, Opening<(), NOTES_TREE_DEPTH>)>,
100        root: BlsScalar,
101        transfer_value: u64,
102        obfuscated_transaction: bool,
103        deposit: u64,
104        gas_limit: u64,
105        gas_price: u64,
106        chain_id: u8,
107        data: Option<impl Into<TransactionData>>,
108        prover: &P,
109    ) -> Result<Self, Error> {
110        Ok(Self::Phoenix(PhoenixTransaction::new::<R, P>(
111            rng,
112            sender_sk,
113            refund_pk,
114            receiver_pk,
115            inputs,
116            root,
117            transfer_value,
118            obfuscated_transaction,
119            deposit,
120            gas_limit,
121            gas_price,
122            chain_id,
123            data,
124            prover,
125        )?))
126    }
127
128    /// Create a new moonlight transaction.
129    ///
130    /// # Errors
131    /// The creation of a transaction is not possible and will error if:
132    /// - the memo, if given, is too large
133    #[allow(clippy::too_many_arguments)]
134    pub fn moonlight(
135        sender_sk: &AccountSecretKey,
136        receiver: Option<AccountPublicKey>,
137        value: u64,
138        deposit: u64,
139        gas_limit: u64,
140        gas_price: u64,
141        nonce: u64,
142        chain_id: u8,
143        data: Option<impl Into<TransactionData>>,
144    ) -> Result<Self, Error> {
145        Ok(Self::Moonlight(MoonlightTransaction::new(
146            sender_sk, receiver, value, deposit, gas_limit, gas_price, nonce,
147            chain_id, data,
148        )?))
149    }
150
151    /// Return the sender of the account for Moonlight transactions.
152    #[must_use]
153    pub fn moonlight_sender(&self) -> Option<&AccountPublicKey> {
154        match self {
155            Self::Phoenix(_) => None,
156            Self::Moonlight(tx) => Some(tx.sender()),
157        }
158    }
159
160    /// Get the receiver of the transaction for Moonlight transactions, if it
161    /// exists.
162    ///
163    /// # Returns
164    ///
165    /// - `Some(&AccountPublicKey)` if the transaction is a Moonlight
166    ///   transaction and the receiver is different from the sender.
167    /// - `None` if the transaction is a Moonlight transaction and the receiver
168    ///   is the same as the sender.
169    /// - `None` if the transaction is a Phoenix transaction.
170    #[must_use]
171    pub fn moonlight_receiver(&self) -> Option<&AccountPublicKey> {
172        match self {
173            Self::Phoenix(_) => None,
174            Self::Moonlight(tx) => tx.receiver(),
175        }
176    }
177
178    /// Return the value transferred in a Moonlight transaction.
179    #[must_use]
180    pub fn value(&self) -> Option<u64> {
181        match self {
182            Self::Phoenix(_) => None,
183            Self::Moonlight(tx) => Some(tx.value()),
184        }
185    }
186
187    /// Returns the nullifiers of the transaction, if the transaction is a
188    /// moonlight transaction, the result will be empty.
189    #[must_use]
190    pub fn nullifiers(&self) -> &[BlsScalar] {
191        match self {
192            Self::Phoenix(tx) => tx.nullifiers(),
193            Self::Moonlight(_) => &[],
194        }
195    }
196
197    /// Return the root of the UTXO tree for Phoenix transactions.
198    #[must_use]
199    pub fn root(&self) -> Option<&BlsScalar> {
200        match self {
201            Self::Phoenix(tx) => Some(tx.root()),
202            Self::Moonlight(_) => None,
203        }
204    }
205
206    /// Return the UTXO outputs of the transaction.
207    #[must_use]
208    pub fn outputs(&self) -> &[Note] {
209        match self {
210            Self::Phoenix(tx) => &tx.outputs()[..],
211            Self::Moonlight(_) => &[],
212        }
213    }
214
215    /// Returns the sender data for Phoenix transactions.
216    #[must_use]
217    pub fn phoenix_sender(&self) -> Option<&Sender> {
218        match self {
219            Self::Phoenix(tx) => Some(tx.sender()),
220            Self::Moonlight(_) => None,
221        }
222    }
223
224    /// Returns the deposit of the transaction.
225    #[must_use]
226    pub fn deposit(&self) -> u64 {
227        match self {
228            Self::Phoenix(tx) => tx.deposit(),
229            Self::Moonlight(tx) => tx.deposit(),
230        }
231    }
232
233    /// Returns the gas limit of the transaction.
234    #[must_use]
235    pub fn gas_limit(&self) -> u64 {
236        match self {
237            Self::Phoenix(tx) => tx.gas_limit(),
238            Self::Moonlight(tx) => tx.gas_limit(),
239        }
240    }
241
242    /// Returns the gas price of the transaction.
243    #[must_use]
244    pub fn gas_price(&self) -> u64 {
245        match self {
246            Self::Phoenix(tx) => tx.gas_price(),
247            Self::Moonlight(tx) => tx.gas_price(),
248        }
249    }
250
251    /// Returns the refund-address of the transaction.
252    #[must_use]
253    pub fn refund_address(&self) -> RefundAddress {
254        match self {
255            Self::Phoenix(tx) => RefundAddress::Phoenix(tx.stealth_address()),
256            Self::Moonlight(tx) => {
257                RefundAddress::Moonlight(tx.refund_address())
258            }
259        }
260    }
261
262    /// Return the contract call data, if there is any.
263    ///
264    /// Call data is present only when inter-contract calls happen.
265    ///
266    /// # Returns
267    ///
268    /// - `Some(&ContractCall)` if the transaction invokes another call to a
269    ///   contract.
270    /// - `None` if the transaction is an entrypoint call to a protocol contract
271    ///   without a second call attached to it.
272    #[must_use]
273    pub fn call(&self) -> Option<&ContractCall> {
274        match self {
275            Self::Phoenix(tx) => tx.call(),
276            Self::Moonlight(tx) => tx.call(),
277        }
278    }
279
280    /// Return the contract deploy data, if there is any.
281    #[must_use]
282    pub fn deploy(&self) -> Option<&ContractDeploy> {
283        match self {
284            Self::Phoenix(tx) => tx.deploy(),
285            Self::Moonlight(tx) => tx.deploy(),
286        }
287    }
288
289    /// Returns the memo used with the transaction, if any.
290    #[must_use]
291    pub fn memo(&self) -> Option<&[u8]> {
292        match self {
293            Self::Phoenix(tx) => tx.memo(),
294            Self::Moonlight(tx) => tx.memo(),
295        }
296    }
297
298    /// Returns the Blob used with the transaction, if any.
299    #[must_use]
300    pub fn blob(&self) -> Option<&Vec<BlobData>> {
301        match self {
302            Self::Phoenix(tx) => tx.blob(),
303            Self::Moonlight(tx) => tx.blob(),
304        }
305    }
306
307    /// Returns the Blob used with the transaction, if any.
308    #[must_use]
309    pub fn blob_mut(&mut self) -> Option<&mut Vec<BlobData>> {
310        match self {
311            Self::Phoenix(tx) => tx.blob_mut(),
312            Self::Moonlight(tx) => tx.blob_mut(),
313        }
314    }
315
316    /// Extracts and removes the blob sidecar from the transaction, if any, and
317    /// returns it as a vector of tuples containing the blob hash and the
318    /// corresponding blob sidecar.
319    ///
320    /// This function mutably accesses the blob storage within the transaction,
321    /// clears the stored data, and returns the extracted parts while
322    /// preserving their hashes.
323    ///
324    /// Returns `None` if there are no blobs present in the transaction.
325    #[must_use]
326    pub fn strip_blobs(&mut self) -> Option<Vec<([u8; 32], BlobSidecar)>> {
327        let blob = match self {
328            Self::Phoenix(tx) => tx.blob_mut(),
329            Self::Moonlight(tx) => tx.blob_mut(),
330        }?;
331
332        let ret = blob
333            .iter_mut()
334            .filter_map(|b| b.take_sidecar().map(|d| (b.hash, d)))
335            .collect::<Vec<_>>();
336
337        Some(ret)
338    }
339
340    /// Creates a modified clone of this transaction if it contains a Blob,
341    /// clones all fields except for the Blob, whose versioned hashes are set as
342    /// Memo.
343    ///
344    /// Returns none if the transaction is not a Blob transaction.
345    #[must_use]
346    pub fn blob_to_memo(&self) -> Option<Self> {
347        Some(match self {
348            Transaction::Phoenix(tx) => {
349                Transaction::Phoenix(tx.blob_to_memo()?)
350            }
351            Transaction::Moonlight(tx) => {
352                Transaction::Moonlight(tx.blob_to_memo()?)
353            }
354        })
355    }
356
357    /// Creates a modified clone of this transaction if it contains data for
358    /// deployment, clones all fields except for the bytecode' 'bytes' part.
359    /// Returns none if the transaction is not a deployment transaction.
360    #[must_use]
361    pub fn strip_off_bytecode(&self) -> Option<Self> {
362        Some(match self {
363            Transaction::Phoenix(tx) => {
364                Transaction::Phoenix(tx.strip_off_bytecode()?)
365            }
366            Transaction::Moonlight(tx) => {
367                Transaction::Moonlight(tx.strip_off_bytecode()?)
368            }
369        })
370    }
371
372    /// Serialize the transaction into a byte buffer.
373    #[must_use]
374    pub fn to_var_bytes(&self) -> Vec<u8> {
375        let mut bytes = Vec::new();
376
377        match self {
378            Self::Phoenix(tx) => {
379                bytes.push(0);
380                bytes.extend(tx.to_var_bytes());
381            }
382            Self::Moonlight(tx) => {
383                bytes.push(1);
384                bytes.extend(tx.to_var_bytes());
385            }
386        }
387
388        bytes
389    }
390
391    /// Deserialize the transaction from a byte slice.
392    ///
393    /// # Errors
394    /// Errors when the bytes are not canonical.
395    pub fn from_slice(buf: &[u8]) -> Result<Self, BytesError> {
396        let mut buf = buf;
397
398        Ok(match u8::from_reader(&mut buf)? {
399            0 => Self::Phoenix(PhoenixTransaction::from_slice(buf)?),
400            1 => Self::Moonlight(MoonlightTransaction::from_slice(buf)?),
401            _ => return Err(BytesError::InvalidData),
402        })
403    }
404
405    /// Return input bytes to hash the transaction.
406    ///
407    /// Note: The result of this function is *only* meant to be used as an input
408    /// for hashing and *cannot* be used to deserialize the transaction again.
409    #[must_use]
410    pub fn to_hash_input_bytes(&self) -> Vec<u8> {
411        match self {
412            Self::Phoenix(tx) => tx.to_hash_input_bytes(),
413            Self::Moonlight(tx) => tx.to_hash_input_bytes(),
414        }
415    }
416
417    /// Create the unique transaction hash.
418    #[must_use]
419    pub fn hash(&self) -> BlsScalar {
420        match self {
421            Self::Phoenix(tx) => tx.hash(),
422            Self::Moonlight(tx) => tx.hash(),
423        }
424    }
425
426    /// Returns the charge for a contract deployment. The deployment of a
427    /// contract will cost at least `min_deploy_points`.
428    /// If the transaction is not a deploy-transaction, the deploy-charge will
429    /// be 0.
430    #[must_use]
431    pub fn deploy_charge(
432        &self,
433        gas_per_deploy_byte: u64,
434        min_deploy_points: u64,
435    ) -> u64 {
436        if let Some(deploy) = self.deploy() {
437            let bytecode_len = deploy.bytecode.bytes.len() as u64;
438            max(bytecode_len * gas_per_deploy_byte, min_deploy_points)
439        } else {
440            0
441        }
442    }
443
444    /// Returns the minimum gas charged for a blob transaction deployment.
445    /// If the transaction is not a blob transaction, it returns None.
446    #[must_use]
447    pub fn blob_charge(&self, gas_per_blob: u64) -> Option<u64> {
448        self.blob().map(|blobs| blobs.len() as u64 * gas_per_blob)
449    }
450
451    /// Check if the transaction is a deployment transaction and if it
452    /// meets the minimum requirements for gas price and gas limit.
453    ///
454    /// # Errors
455    /// Returns an error if the transaction is a deployment transaction and
456    /// the gas price is lower than the minimum required gas price or if the
457    /// gas limit is lower than the required deployment charge.
458    pub fn deploy_check(
459        &self,
460        gas_per_deploy_byte: u64,
461        min_deploy_gas_price: u64,
462        min_deploy_points: u64,
463    ) -> Result<(), TxPreconditionError> {
464        if self.deploy().is_some() {
465            let deploy_charge =
466                self.deploy_charge(gas_per_deploy_byte, min_deploy_points);
467
468            if self.gas_price() < min_deploy_gas_price {
469                return Err(TxPreconditionError::DeployLowPrice(
470                    min_deploy_gas_price,
471                ));
472            }
473            if self.gas_limit() < deploy_charge {
474                return Err(TxPreconditionError::DeployLowLimit(deploy_charge));
475            }
476        }
477
478        Ok(())
479    }
480
481    /// Check if the transaction is a blob transaction and if it meets the
482    /// minimum requirements for gas limit.
483    ///
484    /// # Returns
485    /// - `Ok(Some(u64))` the minimum gas amount to charge if the transaction is
486    ///   a blob transaction.
487    /// - `Ok(None)` if the transaction is not a blob transaction.
488    /// - `Err(TxPreconditionError)` in case of an error.
489    ///
490    /// # Errors
491    /// Returns an error if the transaction is a blob transaction and:
492    /// - the gas limit is lower than the minimum charge.
493    /// - the number of blobs is zero or greater than 6.
494    pub fn blob_check(
495        &self,
496        gas_per_blob: u64,
497    ) -> Result<Option<u64>, TxPreconditionError> {
498        if let Some(blobs) = self.blob() {
499            match blobs.len() {
500                0 => Err(TxPreconditionError::BlobEmpty),
501                n if n > 6 => Err(TxPreconditionError::BlobTooMany(n)),
502                _ => Ok(()),
503            }?;
504        } else {
505            return Ok(None);
506        }
507
508        let min_charge = self.blob_charge(gas_per_blob);
509        if let Some(min_charge) = min_charge {
510            if self.gas_limit() < min_charge {
511                return Err(TxPreconditionError::BlobLowLimit(min_charge));
512            }
513        }
514        Ok(min_charge)
515    }
516}
517
518impl From<PhoenixTransaction> for Transaction {
519    fn from(tx: PhoenixTransaction) -> Self {
520        Self::Phoenix(tx)
521    }
522}
523
524impl From<MoonlightTransaction> for Transaction {
525    fn from(tx: MoonlightTransaction) -> Self {
526        Self::Moonlight(tx)
527    }
528}
529
530/// Enum defining the address to refund unspent gas to for both Phoenix and
531/// Moonlight transactions.
532pub enum RefundAddress<'a> {
533    /// The address of the Phoenix refund note.
534    Phoenix(&'a StealthAddress),
535    /// The moonlight account to which to send the refund.
536    Moonlight(&'a AccountPublicKey),
537}
538
539/// The payload sent by a contract to the transfer contract to transfer some of
540/// its funds to another contract.
541#[derive(Debug, Clone, Archive, PartialEq, Eq, Serialize, Deserialize)]
542#[archive_attr(derive(CheckBytes))]
543#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
544#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
545pub struct ContractToContract {
546    /// Contract to transfer funds to.
547    pub contract: ContractId,
548    /// Amount to send to the contract.
549    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
550    pub value: u64,
551    /// Function name to call on the contract.
552    pub fn_name: String,
553    /// Extra data sent along with [`ReceiveFromContract`]
554    #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
555    pub data: Vec<u8>,
556}
557
558/// The payload sent by the transfer contract to a contract receiving funds from
559/// another contract.
560#[derive(Debug, Clone, Archive, PartialEq, Eq, Serialize, Deserialize)]
561#[archive_attr(derive(CheckBytes))]
562#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
563#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
564pub struct ReceiveFromContract {
565    /// Contract that sent the funds.
566    pub contract: ContractId,
567    /// Amount sent by the contract.
568    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
569    pub value: u64,
570    /// Extra data sent by the sender.
571    #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
572    pub data: Vec<u8>,
573}
574
575/// The payload sent by a contract to the transfer contract to transfer some of
576/// its funds to an account.
577#[derive(Debug, Clone, Archive, PartialEq, Eq, Serialize, Deserialize)]
578#[archive_attr(derive(CheckBytes))]
579#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
580#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
581pub struct ContractToAccount {
582    /// Account to transfer funds to.
583    pub account: AccountPublicKey,
584    /// Amount to send to the account.
585    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
586    pub value: u64,
587}
588
589/// Event data emitted on a withdrawal from a contract.
590#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
591#[archive_attr(derive(CheckBytes))]
592#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
593#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
594pub struct WithdrawEvent {
595    /// The contract withdrawn from.
596    pub sender: ContractId,
597    /// The receiver of the value.
598    pub receiver: WithdrawReceiver,
599    /// The value withdrawn.
600    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
601    pub value: u64,
602}
603
604impl From<Withdraw> for WithdrawEvent {
605    fn from(w: Withdraw) -> Self {
606        Self {
607            sender: w.contract,
608            receiver: w.receiver,
609            value: w.value,
610        }
611    }
612}
613
614/// Event data emitted on a conversion from Phoenix to Moonlight, and
615/// vice-versa.
616#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
617#[archive_attr(derive(CheckBytes))]
618#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
619#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
620pub struct ConvertEvent {
621    /// The originator of the conversion, if it is possible to determine. From
622    /// Moonlight to Phoenix it is possible, but the same cannot be done the
623    /// other way round.
624    pub sender: Option<AccountPublicKey>,
625    /// The receiver of the value.
626    pub receiver: WithdrawReceiver,
627    /// The value converted.
628    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
629    pub value: u64,
630}
631
632impl ConvertEvent {
633    /// Convert a sender and a withdraw into a conversion event.
634    #[must_use]
635    pub fn from_withdraw_and_sender(
636        sender: Option<AccountPublicKey>,
637        withdraw: &Withdraw,
638    ) -> Self {
639        Self {
640            sender,
641            receiver: withdraw.receiver,
642            value: withdraw.value,
643        }
644    }
645}
646
647/// Event data emitted on a deposit to a contract.
648#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
649#[archive_attr(derive(CheckBytes))]
650#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
651#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
652pub struct DepositEvent {
653    /// The originator of the deposit, if it is possible to determine. If the
654    /// depositor is using Moonlight this will be available. If they're using
655    /// Phoenix it will not.
656    pub sender: Option<AccountPublicKey>,
657    /// The receiver of the value.
658    pub receiver: ContractId,
659    /// The value deposited.
660    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
661    pub value: u64,
662}
663
664/// Event data emitted on a transfer from a contract to a contract.
665#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
666#[archive_attr(derive(CheckBytes))]
667#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
668#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
669pub struct ContractToContractEvent {
670    /// The sender of the funds.
671    pub sender: ContractId,
672    /// The receiver of the funds.
673    pub receiver: ContractId,
674    /// The value transferred.
675    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
676    pub value: u64,
677}
678
679/// Event data emitted on a transfer from a contract to a Moonlight account.
680#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
681#[archive_attr(derive(CheckBytes))]
682#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
683#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
684pub struct ContractToAccountEvent {
685    /// The sender of the funds.
686    pub sender: ContractId,
687    /// The receiver of the funds.
688    pub receiver: AccountPublicKey,
689    /// The value transferred.
690    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
691    pub value: u64,
692}
693
694/// Event data emitted on a phoenix transaction's completion.
695#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
696#[archive_attr(derive(CheckBytes))]
697#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
698#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
699pub struct PhoenixTransactionEvent {
700    /// Nullifiers of the notes spent during the transaction.
701    pub nullifiers: Vec<BlsScalar>,
702    /// Notes produced during the transaction.
703    pub notes: Vec<Note>,
704    /// The memo included in the transaction.
705    #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
706    pub memo: Vec<u8>,
707    /// Gas spent by the transaction.
708    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
709    pub gas_spent: u64,
710
711    /// Optional gas-refund note if the refund is positive.
712    pub refund_note: Option<Note>,
713}
714
715/// Event data emitted on a moonlight transaction's completion.
716#[derive(Debug, Clone, Archive, PartialEq, Serialize, Deserialize)]
717#[archive_attr(derive(CheckBytes))]
718#[cfg_attr(feature = "serde", cfg_eval, serde_as)]
719#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
720pub struct MoonlightTransactionEvent {
721    /// The account that initiated the transaction.
722    pub sender: AccountPublicKey,
723    /// The receiver of the funds if any were transferred.
724    pub receiver: Option<AccountPublicKey>,
725    /// Transfer amount
726    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
727    pub value: u64,
728    /// The memo included in the transaction.
729    #[cfg_attr(feature = "serde", serde_as(as = "Hex"))]
730    pub memo: Vec<u8>,
731    /// Gas spent by the transaction.
732    #[cfg_attr(feature = "serde", serde_as(as = "DisplayFromStr"))]
733    pub gas_spent: u64,
734    /// Optional refund-info in the case that the refund-address is different
735    /// from the sender.
736    #[cfg_attr(
737        feature = "serde",
738        serde_as(as = "Option<(serde_with::Same, DisplayFromStr)>")
739    )]
740    pub refund_info: Option<(AccountPublicKey, u64)>,
741}