Skip to main content

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