token-2022: Add confidential transfer with fee instruction (#2988)

* token-2022: add separate transfer with fee instruction

* token-2022: add `TransferWithFee` client code

* apply twoxtx patch

* token-2022: add brief tests for transfer with fee

* Revert "apply twoxtx patch"

This reverts commit ce09d1f5d2fb496cd4ee9991be234726786e39f2.

* token-2022: cargo fmt

* token-2022: uncommenting the rest of the tests

* token-2022: cargo fmt

* token-2022: temporarily reverting to 5f89521

* token-2022: minor

* token-2022: clippy

* token-2022: apply twoxtx patch

* token-2022: fix transfer with fee test

* Revert "token-2022: apply twoxtx patch"

This reverts commit 577e63c2f38ce0a17fa4aede3d0acfd852b1d3ab.

* token-2022: simplify fee parameter for zkp on client

* token-2022: fix build
This commit is contained in:
samkim-crypto 2022-04-22 14:00:42 -04:00 committed by GitHub
parent b5e301b210
commit 049a89f351
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 439 additions and 59 deletions

90
Cargo.lock generated
View File

@ -166,9 +166,9 @@ dependencies = [
[[package]]
name = "async-trait"
version = "0.1.52"
version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3"
checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600"
dependencies = [
"proc-macro2 1.0.36",
"quote 1.0.14",
@ -245,7 +245,7 @@ dependencies = [
"getrandom 0.2.3",
"instant",
"pin-project-lite",
"rand 0.8.4",
"rand 0.8.5",
"tokio",
]
@ -1296,9 +1296,9 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.0.22"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af"
dependencies = [
"cfg-if 1.0.0",
"crc32fast",
@ -1621,9 +1621,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hidapi"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "253bfb01a7a31d71212dc3e920540546fa4a4fe08e2d87fc3299c42bae7ee2f9"
checksum = "38b1717343691998deb81766bfcd1dce6df0d5d6c37070b5a3de2bb6d39f7822"
dependencies = [
"cc",
"libc",
@ -1937,9 +1937,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.55"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
dependencies = [
"wasm-bindgen",
]
@ -2087,9 +2087,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.121"
version = "0.2.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
[[package]]
name = "libloading"
@ -2321,12 +2321,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.4.4"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
dependencies = [
"adler",
"autocfg",
]
[[package]]
@ -2615,7 +2614,7 @@ dependencies = [
"lazy_static",
"percent-encoding 2.1.0",
"pin-project",
"rand 0.8.4",
"rand 0.8.5",
"thiserror",
]
@ -2970,7 +2969,7 @@ dependencies = [
"lazy_static",
"num-traits",
"quick-error 2.0.1",
"rand 0.8.4",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rand_xorshift",
"regex-syntax",
@ -3129,7 +3128,7 @@ checksum = "359c5eb33845f3ee05c229e65f87cdbc503eea394964b8f1330833d460b4ff3e"
dependencies = [
"bytes",
"fxhash",
"rand 0.8.4",
"rand 0.8.5",
"ring",
"rustls",
"rustls-native-certs",
@ -3184,20 +3183,19 @@ dependencies = [
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
"rand_hc",
"rand_pcg",
]
[[package]]
name = "rand"
version = "0.8.4"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
"rand_hc 0.3.1",
]
[[package]]
@ -3247,15 +3245,6 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core 0.6.3",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
@ -3285,9 +3274,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.5.1"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221"
dependencies = [
"autocfg",
"crossbeam-deque",
@ -3297,14 +3286,13 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.9.1"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
@ -3949,7 +3937,7 @@ dependencies = [
"futures 0.3.21",
"httparse",
"log",
"rand 0.8.4",
"rand 0.8.5",
"sha-1 0.9.8",
]
@ -5942,7 +5930,7 @@ dependencies = [
"humantime",
"opentelemetry",
"pin-project",
"rand 0.8.4",
"rand 0.8.5",
"serde",
"static_assertions",
"tarpc-plugins",
@ -6341,7 +6329,7 @@ dependencies = [
"indexmap",
"pin-project",
"pin-project-lite",
"rand 0.8.4",
"rand 0.8.5",
"slab",
"tokio",
"tokio-util 0.7.1",
@ -6471,7 +6459,7 @@ dependencies = [
"http",
"httparse",
"log",
"rand 0.8.4",
"rand 0.8.5",
"rustls",
"sha-1 0.10.0",
"thiserror",
@ -6595,9 +6583,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "uriparse"
version = "0.6.3"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e515b1ada404168e145ac55afba3c42f04cf972201a8552d42e2abb17c1b7221"
checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff"
dependencies = [
"fnv",
"lazy_static",
@ -6716,9 +6704,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.78"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
@ -6726,9 +6714,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.78"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
dependencies = [
"bumpalo",
"lazy_static",
@ -6753,9 +6741,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.78"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
dependencies = [
"quote 1.0.14",
"wasm-bindgen-macro-support",
@ -6763,9 +6751,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.78"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
dependencies = [
"proc-macro2 1.0.36",
"quote 1.0.14",
@ -6776,9 +6764,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.78"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
[[package]]
name = "web-sys"

View File

@ -2,6 +2,7 @@ use crate::client::{ProgramClient, ProgramClientError, SendTransaction};
use solana_program_test::tokio::time;
use solana_sdk::{
account::Account as BaseAccount,
epoch_info::EpochInfo,
hash::Hash,
instruction::Instruction,
program_error::ProgramError,
@ -22,6 +23,7 @@ use spl_token_2022::{
solana_zk_token_sdk::{
encryption::{auth_encryption::*, elgamal::*},
errors::ProofError,
instruction::transfer_with_fee::FeeParameters,
},
state::{Account, AccountState, Mint},
};
@ -1148,6 +1150,80 @@ where
.await
}
/// Transfer tokens confidentially with fee
#[allow(clippy::too_many_arguments)]
pub async fn confidential_transfer_transfer_with_fee<S2: Signer>(
&self,
source_token_account: &Pubkey,
destination_token_account: &Pubkey,
source_token_authority: &S2,
amount: u64,
source_available_balance: u64,
source_elgamal_keypair: &ElGamalKeypair,
new_source_decryptable_available_balance: AeCiphertext,
epoch_info: &EpochInfo,
) -> TokenResult<T::Output> {
let source_state = self.get_account_info(source_token_account).await.unwrap();
let source_extension =
source_state.get_extension::<confidential_transfer::ConfidentialTransferAccount>()?;
let destination_state = self
.get_account_info(destination_token_account)
.await
.unwrap();
let destination_extension = destination_state
.get_extension::<confidential_transfer::ConfidentialTransferAccount>(
)?;
let mint_state = self.get_mint_info().await.unwrap();
let transfer_fee_config = mint_state
.get_extension::<transfer_fee::TransferFeeConfig>()
.unwrap();
let fee_parameters = transfer_fee_config.get_epoch_fee(epoch_info.epoch);
let ct_mint = mint_state
.get_extension::<confidential_transfer::ConfidentialTransferMint>()
.unwrap();
let proof_data = confidential_transfer::instruction::TransferWithFeeData::new(
amount,
(
source_available_balance,
&source_extension.available_balance.try_into().unwrap(),
),
source_elgamal_keypair,
(
&destination_extension.encryption_pubkey.try_into().unwrap(),
&ct_mint.auditor_pubkey.try_into().unwrap(),
),
FeeParameters {
fee_rate_basis_points: u16::from(fee_parameters.transfer_fee_basis_points),
maximum_fee: u64::from(fee_parameters.maximum_fee),
},
&ct_mint
.withdraw_withheld_authority_pubkey
.try_into()
.unwrap(),
)
.map_err(TokenError::Proof)?;
self.process_ixs(
&confidential_transfer::instruction::transfer_with_fee(
&self.program_id,
source_token_account,
destination_token_account,
&self.pubkey,
new_source_decryptable_available_balance,
&source_token_authority.pubkey(),
&[],
&proof_data,
)?,
&[source_token_authority],
)
.await
}
/// Applies the confidential transfer pending balance to the available balance
pub async fn confidential_transfer_apply_pending_balance<S2: Signer>(
&self,

View File

@ -6,10 +6,11 @@ use {
program_test::{TestContext, TokenContext},
solana_program_test::tokio,
solana_sdk::{
instruction::InstructionError, pubkey::Pubkey, signature::Signer, signer::keypair::Keypair,
transaction::TransactionError, transport::TransportError,
epoch_info::EpochInfo, instruction::InstructionError, pubkey::Pubkey, signature::Signer,
signer::keypair::Keypair, transaction::TransactionError, transport::TransportError,
},
spl_token_2022::{
error::TokenError,
extension::{
confidential_transfer::{
ConfidentialTransferAccount, ConfidentialTransferMint, EncryptedWithheldAmount,
@ -28,6 +29,20 @@ use {
std::convert::TryInto,
};
const TEST_MAXIMUM_FEE: u64 = 100;
const TEST_FEE_BASIS_POINTS: u16 = 250;
fn test_epoch_info() -> EpochInfo {
EpochInfo {
epoch: 0,
slot_index: 0,
slots_in_epoch: 0,
absolute_slot: 0,
block_height: 0,
transaction_count: None,
}
}
struct ConfidentialTransferMintWithKeypairs {
ct_mint: ConfidentialTransferMint,
ct_mint_authority: Keypair,
@ -703,3 +718,194 @@ async fn ct_transfer() {
Some(42),
);
}
#[tokio::test]
async fn ct_transfer_with_fee() {
let ConfidentialTransferMintWithKeypairs { ct_mint, .. } =
ConfidentialTransferMintWithKeypairs::new();
let mut context = TestContext::new().await;
context
.init_token_with_mint(vec![
ExtensionInitializationParams::TransferFeeConfig {
transfer_fee_config_authority: Some(Pubkey::new_unique()),
withdraw_withheld_authority: Some(Pubkey::new_unique()),
transfer_fee_basis_points: TEST_FEE_BASIS_POINTS,
maximum_fee: TEST_MAXIMUM_FEE,
},
ExtensionInitializationParams::ConfidentialTransferMint { ct_mint },
])
.await
.unwrap();
let TokenContext {
token,
alice,
bob,
mint_authority,
decimals,
..
} = context.token_context.unwrap();
let epoch_info = test_epoch_info();
let alice_meta =
ConfidentialTokenAccountMeta::with_tokens(&token, &alice, &mint_authority, 100, decimals)
.await;
let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob).await;
// Self-transfer of 0 tokens
token
.confidential_transfer_transfer_with_fee(
&alice_meta.token_account,
&alice_meta.token_account,
&alice,
0, // amount
100, // available balance
&alice_meta.elgamal_keypair,
alice_meta.ae_key.encrypt(100_u64),
&epoch_info,
)
.await
.unwrap();
// Self-transfer of N tokens
token
.confidential_transfer_transfer_with_fee(
&alice_meta.token_account,
&alice_meta.token_account,
&alice,
100, // amount
100, // available balance
&alice_meta.elgamal_keypair,
alice_meta.ae_key.encrypt(100_u64),
&epoch_info,
)
.await
.unwrap();
// Fee is 2.5%, so what is left in 97 in Alice account
token
.confidential_transfer_apply_pending_balance(
&alice_meta.token_account,
&alice,
2,
alice_meta.ae_key.encrypt(97_u64),
)
.await
.unwrap();
let state = token
.get_account_info(&alice_meta.token_account)
.await
.unwrap();
let extension = state
.get_extension::<ConfidentialTransferAccount>()
.unwrap();
assert_eq!(
alice_meta
.ae_key
.decrypt(&extension.decryptable_available_balance.try_into().unwrap()),
Some(97),
);
token
.confidential_transfer_transfer_with_fee(
&alice_meta.token_account,
&bob_meta.token_account,
&alice,
97, // amount
97, // available balance
&alice_meta.elgamal_keypair,
alice_meta.ae_key.encrypt(0_u64),
&epoch_info,
)
.await
.unwrap();
let state = token
.get_account_info(&alice_meta.token_account)
.await
.unwrap();
let extension = state
.get_extension::<ConfidentialTransferAccount>()
.unwrap();
assert_eq!(
alice_meta
.ae_key
.decrypt(&extension.decryptable_available_balance.try_into().unwrap()),
Some(0),
);
// Alice account cannot be closed since there are withheld fees from self-transfer
let error = token
.confidential_transfer_empty_account(
&alice_meta.token_account,
&alice,
&alice_meta.elgamal_keypair,
)
.await
.unwrap_err();
assert_eq!(
error,
TokenClientError::Client(Box::new(TransportError::TransactionError(
TransactionError::InstructionError(
1,
InstructionError::Custom(TokenError::ConfidentialTransferAccountHasBalance as u32)
)
)))
);
let err = token
.confidential_transfer_empty_account(
&bob_meta.token_account,
&bob,
&bob_meta.elgamal_keypair,
)
.await
.unwrap_err();
assert_eq!(
err,
TokenClientError::Client(Box::new(TransportError::TransactionError(
TransactionError::InstructionError(1, InstructionError::InvalidAccountData)
)))
);
let state = token
.get_account_info(&bob_meta.token_account)
.await
.unwrap();
let extension = state
.get_extension::<ConfidentialTransferAccount>()
.unwrap();
assert_eq!(
bob_meta
.ae_key
.decrypt(&extension.decryptable_available_balance.try_into().unwrap()),
Some(0),
);
token
.confidential_transfer_apply_pending_balance(
&bob_meta.token_account,
&bob,
1,
bob_meta.ae_key.encrypt(94_u64),
)
.await
.unwrap();
let state = token
.get_account_info(&bob_meta.token_account)
.await
.unwrap();
let extension = state
.get_extension::<ConfidentialTransferAccount>()
.unwrap();
assert_eq!(
bob_meta
.ae_key
.decrypt(&extension.decryptable_available_balance.try_into().unwrap()),
Some(94),
);
}

View File

@ -201,6 +201,28 @@ pub enum ConfidentialTransferInstruction {
///
Transfer,
/// Transfer tokens confidentially with fee.
///
/// * Single owner/delegate
/// 1. `[writable]` The source SPL Token account.
/// 2. `[writable]` The destination SPL Token account.
/// 3. `[]` The token mint.
/// 4. `[]` Instructions sysvar.
/// 5. `[signer]` The single source account owner.
///
/// * Multisignature owner/delegate
/// 1. `[writable]` The source SPL Token account.
/// 2. `[writable]` The destination SPL Token account.
/// 3. `[]` The token mint.
/// 4. `[]` Instructions sysvar.
/// 5. `[]` The multisig source account owner.
/// 6.. `[signer]` Required M signer accounts for the SPL Token Multisig account.
///
/// Data expected by this instruction:
/// `TransferWithFeeInstructionData`
///
TransferWithFee,
/// Applies the pending balance to the available balance, based on the history of `Deposit`
/// and/or `Transfer` instructions.
///
@ -404,6 +426,17 @@ pub struct TransferInstructionData {
pub proof_instruction_offset: i8,
}
/// Data expected by `ConfidentialTransferInstruction::TransferWithFee`
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct TransferWithFeeInstructionData {
/// The new source decryptable balance if the transfer succeeds
pub new_source_decryptable_available_balance: DecryptableBalance,
/// Relative location of the `ProofInstruction::VerifyTransfer` instruction to the
/// `Transfer` instruction in the transaction
pub proof_instruction_offset: i8,
}
/// Data expected by `ConfidentialTransferInstruction::ApplyPendingBalance`
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
@ -763,6 +796,73 @@ pub fn transfer(
])
}
/// Create a inner `TransferWithFee` instruction
///
/// This instruction is suitable for use with a cross-program `invoke`
#[allow(clippy::too_many_arguments)]
pub fn inner_transfer_with_fee(
token_program_id: &Pubkey,
source_token_account: &Pubkey,
destination_token_account: &Pubkey,
mint: &Pubkey,
new_source_decryptable_available_balance: DecryptableBalance,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_instruction_offset: i8,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*source_token_account, false),
AccountMeta::new(*destination_token_account, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(sysvar::instructions::id(), false),
AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
];
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::ConfidentialTransferExtension,
ConfidentialTransferInstruction::TransferWithFee,
&TransferWithFeeInstructionData {
new_source_decryptable_available_balance,
proof_instruction_offset,
},
))
}
/// Create a `Transfer` instruction
#[allow(clippy::too_many_arguments)]
#[cfg(not(target_arch = "bpf"))]
pub fn transfer_with_fee(
token_program_id: &Pubkey,
source_token_account: &Pubkey,
destination_token_account: &Pubkey,
mint: &Pubkey,
new_source_decryptable_available_balance: AeCiphertext,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
proof_data: &TransferWithFeeData,
) -> Result<Vec<Instruction>, ProgramError> {
Ok(vec![
verify_transfer_with_fee(proof_data),
inner_transfer_with_fee(
token_program_id,
source_token_account,
destination_token_account,
mint,
new_source_decryptable_available_balance.into(),
authority,
multisig_signers,
-1,
)?, // calls check_program_account
])
}
/// Create a inner `ApplyPendingBalance` instruction
///
/// This instruction is suitable for use with a cross-program `invoke`

View File

@ -33,6 +33,10 @@ fn decode_proof_instruction<T: Pod>(
expected: ProofInstruction,
instruction: &Instruction,
) -> Result<&T, ProgramError> {
if ProofInstruction::decode_type(&instruction.data) != Some(expected) {
msg!("decode type failed ----------------------------------");
}
if instruction.program_id != zk_token_proof_program::id()
|| ProofInstruction::decode_type(&instruction.data) != Some(expected)
{
@ -241,11 +245,6 @@ fn process_empty_account(
}
confidential_transfer_account.available_balance = EncryptedBalance::zeroed();
if confidential_transfer_account.withheld_amount != EncryptedWithheldAmount::zeroed() {
msg!("Withheld amount is not zero");
return Err(ProgramError::InvalidAccountData);
}
confidential_transfer_account.closable()?;
Ok(())
@ -482,7 +481,7 @@ fn process_transfer(
if let Ok(transfer_fee_config) = mint.get_extension::<TransferFeeConfig>() {
// mint is extended for fees
let proof_data = decode_proof_instruction::<TransferWithFeeData>(
ProofInstruction::VerifyTransfer,
ProofInstruction::VerifyTransferWithFee,
&previous_instruction,
)?;
@ -578,6 +577,7 @@ fn process_transfer(
proof_data.ciphertext_hi.commitment,
proof_data.ciphertext_hi.source_handle,
));
process_source_for_transfer(
program_id,
token_account_info,
@ -1162,6 +1162,16 @@ pub(crate) fn process_instruction(
#[cfg(not(feature = "zk-ops"))]
Err(ProgramError::InvalidInstructionData)
}
ConfidentialTransferInstruction::TransferWithFee => {
msg!("ConfidentialTransferInstruction::TransferWithFee");
let data = decode_instruction_data::<TransferWithFeeInstructionData>(input)?;
process_transfer(
program_id,
accounts,
data.new_source_decryptable_available_balance,
data.proof_instruction_offset as i64,
)
}
ConfidentialTransferInstruction::ApplyPendingBalance => {
msg!("ConfidentialTransferInstruction::ApplyPendingBalance");
#[cfg(feature = "zk-ops")]