mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-14 03:59:30 +00:00
refactor: use faucet program to manage faucet account
This commit is contained in:
parent
fe047e169c
commit
28904fe611
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -2666,6 +2666,14 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "faucet_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nssa_core",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ferroid"
|
||||
version = "2.0.0"
|
||||
@ -3938,6 +3946,7 @@ dependencies = [
|
||||
"bytesize",
|
||||
"common",
|
||||
"env_logger",
|
||||
"faucet_core",
|
||||
"futures",
|
||||
"hex",
|
||||
"indexer_ffi",
|
||||
@ -6322,6 +6331,7 @@ dependencies = [
|
||||
"borsh",
|
||||
"clock_core",
|
||||
"env_logger",
|
||||
"faucet_core",
|
||||
"hex",
|
||||
"hex-literal 1.1.0",
|
||||
"k256",
|
||||
@ -7057,6 +7067,7 @@ dependencies = [
|
||||
"ata_program",
|
||||
"authenticated_transfer_core",
|
||||
"clock_core",
|
||||
"faucet_core",
|
||||
"nssa_core",
|
||||
"risc0-zkvm",
|
||||
"serde",
|
||||
@ -8421,6 +8432,7 @@ dependencies = [
|
||||
"bytesize",
|
||||
"chrono",
|
||||
"common",
|
||||
"faucet_core",
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
|
||||
@ -21,6 +21,7 @@ members = [
|
||||
"programs/associated_token_account/core",
|
||||
"programs/associated_token_account",
|
||||
"programs/authenticated_transfer/core",
|
||||
"programs/faucet/core",
|
||||
"programs/vault/core",
|
||||
"sequencer/core",
|
||||
"sequencer/service",
|
||||
@ -68,6 +69,7 @@ amm_program = { path = "programs/amm" }
|
||||
ata_core = { path = "programs/associated_token_account/core" }
|
||||
ata_program = { path = "programs/associated_token_account" }
|
||||
authenticated_transfer_core = { path = "programs/authenticated_transfer/core" }
|
||||
faucet_core = { path = "programs/faucet/core" }
|
||||
vault_core = { path = "programs/vault/core" }
|
||||
test_program_methods = { path = "test_program_methods" }
|
||||
testnet_initial_state = { path = "testnet_initial_state" }
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
artifacts/program_methods/faucet.bin
Normal file
BIN
artifacts/program_methods/faucet.bin
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -68,7 +68,8 @@ impl NSSATransaction {
|
||||
|
||||
/// Validates the transaction against the current state and returns the resulting diff
|
||||
/// without applying it. Rejects transactions that modify clock system accounts and
|
||||
/// rejects unsafe modifications of the system faucet account.
|
||||
/// rejects unsafe modifications of the system faucet account. Also rejects direct
|
||||
/// invocation of the faucet program for user-submitted transactions.
|
||||
///
|
||||
/// This check is required for all user transactions. Only sequencer transaction may bypass this
|
||||
/// check.
|
||||
@ -78,6 +79,14 @@ impl NSSATransaction {
|
||||
block_id: BlockId,
|
||||
timestamp: Timestamp,
|
||||
) -> Result<ValidatedStateDiff, nssa::error::NssaError> {
|
||||
if let Self::Public(tx) = self
|
||||
&& tx.message().program_id == nssa::program::Program::faucet().id()
|
||||
{
|
||||
return Err(nssa::error::NssaError::InvalidInput(
|
||||
"Transaction invokes restricted faucet program".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let diff = match self {
|
||||
Self::Public(tx) => {
|
||||
ValidatedStateDiff::from_public_transaction(tx, state, block_id, timestamp)
|
||||
@ -102,36 +111,6 @@ impl NSSATransaction {
|
||||
));
|
||||
}
|
||||
|
||||
let faucet_account_id = nssa::SYSTEM_FAUCET_ACCOUNT_ID;
|
||||
if let Some(post_faucet) = public_diff.get(&faucet_account_id) {
|
||||
let pre_faucet = state.get_account_by_id(faucet_account_id);
|
||||
|
||||
let nssa::Account {
|
||||
program_owner: post_program_owner,
|
||||
data: post_data,
|
||||
nonce: post_nonce,
|
||||
balance: post_balance,
|
||||
} = post_faucet;
|
||||
|
||||
let nssa::Account {
|
||||
program_owner: pre_program_owner,
|
||||
data: pre_data,
|
||||
nonce: pre_nonce,
|
||||
balance: pre_balance,
|
||||
} = pre_faucet;
|
||||
|
||||
let faucet_change_is_allowed = *post_program_owner == pre_program_owner
|
||||
&& *post_data == pre_data
|
||||
&& *post_nonce == pre_nonce
|
||||
&& *post_balance >= pre_balance;
|
||||
|
||||
if !faucet_change_is_allowed {
|
||||
return Err(nssa::error::NssaError::InvalidInput(
|
||||
"Transaction modifies system faucet account".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(diff)
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ serde_json.workspace = true
|
||||
token_core.workspace = true
|
||||
ata_core.workspace = true
|
||||
vault_core.workspace = true
|
||||
faucet_core.workspace = true
|
||||
indexer_service_rpc = { workspace = true, features = ["client"] }
|
||||
sequencer_service_rpc = { workspace = true, features = ["client"] }
|
||||
jsonrpsee = { workspace = true, features = ["ws-client"] }
|
||||
|
||||
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||
use common::transaction::NSSATransaction;
|
||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, public_mention};
|
||||
use log::info;
|
||||
use nssa::{SYSTEM_FAUCET_ACCOUNT_ID, program::Program, public_transaction};
|
||||
use nssa::{program::Program, public_transaction, system_faucet_account_id};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::{
|
||||
@ -349,6 +349,7 @@ async fn successful_transfer_using_to_label() -> Result<()> {
|
||||
#[test]
|
||||
async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
|
||||
let recipient = ctx.existing_public_accounts()[0];
|
||||
let recipient_balance_before = ctx
|
||||
@ -357,13 +358,13 @@ async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
|
||||
.await?;
|
||||
let faucet_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(SYSTEM_FAUCET_ACCOUNT_ID)
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
|
||||
let amount = 1_u128;
|
||||
let message = public_transaction::Message::try_new(
|
||||
Program::authenticated_transfer_program().id(),
|
||||
vec![SYSTEM_FAUCET_ACCOUNT_ID, recipient],
|
||||
vec![faucet_account_id, recipient],
|
||||
vec![],
|
||||
authenticated_transfer_core::Instruction::Transfer { amount },
|
||||
)?;
|
||||
@ -385,7 +386,7 @@ async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
|
||||
.await?;
|
||||
let faucet_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(SYSTEM_FAUCET_ACCOUNT_ID)
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
|
||||
|
||||
@ -399,18 +400,19 @@ async fn cannot_transfer_funds_from_system_faucet_account() -> Result<()> {
|
||||
#[test]
|
||||
async fn can_transfer_funds_to_system_faucet_account() -> Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
|
||||
let sender = ctx.existing_public_accounts()[0];
|
||||
let sender_balance_before = ctx.sequencer_client().get_account_balance(sender).await?;
|
||||
let faucet_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(SYSTEM_FAUCET_ACCOUNT_ID)
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
|
||||
let amount = 100_u128;
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
from: public_mention(sender),
|
||||
to: Some(public_mention(SYSTEM_FAUCET_ACCOUNT_ID)),
|
||||
to: Some(public_mention(faucet_account_id)),
|
||||
to_npk: None,
|
||||
to_vpk: None,
|
||||
to_identifier: Some(0),
|
||||
@ -424,7 +426,7 @@ async fn can_transfer_funds_to_system_faucet_account() -> Result<()> {
|
||||
let sender_balance_after = ctx.sequencer_client().get_account_balance(sender).await?;
|
||||
let faucet_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(SYSTEM_FAUCET_ACCOUNT_ID)
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
|
||||
assert_eq!(sender_balance_after, sender_balance_before - amount);
|
||||
@ -432,3 +434,61 @@ async fn can_transfer_funds_to_system_faucet_account() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn cannot_execute_faucet_program() -> Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
|
||||
let recipient = ctx.existing_public_accounts()[0];
|
||||
let vault_program_id = Program::vault().id();
|
||||
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient);
|
||||
|
||||
let recipient_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(recipient)
|
||||
.await?;
|
||||
let faucet_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
|
||||
let amount = 1_u128;
|
||||
let message = public_transaction::Message::try_new(
|
||||
Program::faucet().id(),
|
||||
vec![faucet_account_id, recipient_vault_id],
|
||||
vec![],
|
||||
faucet_core::Instruction::Transfer {
|
||||
vault_program_id,
|
||||
recipient_id: recipient,
|
||||
amount,
|
||||
},
|
||||
)?;
|
||||
let tx = nssa::PublicTransaction::new(
|
||||
message,
|
||||
nssa::public_transaction::WitnessSet::from_raw_parts(vec![]),
|
||||
);
|
||||
let tx_hash = ctx
|
||||
.sequencer_client()
|
||||
.send_transaction(NSSATransaction::Public(tx))
|
||||
.await?;
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let recipient_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(recipient)
|
||||
.await?;
|
||||
let faucet_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(faucet_account_id)
|
||||
.await?;
|
||||
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
|
||||
|
||||
assert_eq!(recipient_balance_after, recipient_balance_before);
|
||||
assert_eq!(faucet_balance_after, faucet_balance_before);
|
||||
assert!(tx_on_chain.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ workspace = true
|
||||
[dependencies]
|
||||
nssa_core = { workspace = true, features = ["host"] }
|
||||
clock_core.workspace = true
|
||||
faucet_core.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
)]
|
||||
|
||||
pub use nssa_core::{
|
||||
GENESIS_BLOCK_ID, SYSTEM_FAUCET_ACCOUNT_ID, SharedSecretKey,
|
||||
GENESIS_BLOCK_ID, SharedSecretKey,
|
||||
account::{Account, AccountId, Data},
|
||||
encryption::EphemeralPublicKey,
|
||||
program::ProgramId,
|
||||
@ -18,7 +18,7 @@ pub use public_transaction::PublicTransaction;
|
||||
pub use signature::{PrivateKey, PublicKey, Signature};
|
||||
pub use state::{
|
||||
CLOCK_01_PROGRAM_ACCOUNT_ID, CLOCK_10_PROGRAM_ACCOUNT_ID, CLOCK_50_PROGRAM_ACCOUNT_ID,
|
||||
CLOCK_PROGRAM_ACCOUNT_IDS, V03State,
|
||||
CLOCK_PROGRAM_ACCOUNT_IDS, V03State, system_faucet_account_id,
|
||||
};
|
||||
pub use validated_state_diff::ValidatedStateDiff;
|
||||
|
||||
|
||||
@ -10,8 +10,8 @@ use crate::{
|
||||
error::NssaError,
|
||||
program_methods::{
|
||||
AMM_ELF, AMM_ID, ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID,
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, PINATA_ELF,
|
||||
PINATA_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID,
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, FAUCET_ELF,
|
||||
FAUCET_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID,
|
||||
},
|
||||
};
|
||||
|
||||
@ -156,6 +156,14 @@ impl Program {
|
||||
elf: VAULT_ELF.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn faucet() -> Self {
|
||||
Self {
|
||||
id: FAUCET_ID,
|
||||
elf: FAUCET_ELF.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
|
||||
@ -186,8 +194,9 @@ mod tests {
|
||||
program::Program,
|
||||
program_methods::{
|
||||
AMM_ELF, AMM_ID, ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID,
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, PINATA_ELF,
|
||||
PINATA_ID, PINATA_TOKEN_ELF, PINATA_TOKEN_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID,
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, CLOCK_ELF, CLOCK_ID, FAUCET_ELF,
|
||||
FAUCET_ID, PINATA_ELF, PINATA_ID, PINATA_TOKEN_ELF, PINATA_TOKEN_ID, TOKEN_ELF,
|
||||
TOKEN_ID, VAULT_ELF, VAULT_ID,
|
||||
},
|
||||
};
|
||||
|
||||
@ -501,6 +510,7 @@ mod tests {
|
||||
let auth_transfer_program = Program::authenticated_transfer_program();
|
||||
let token_program = Program::token();
|
||||
let vault_program = Program::vault();
|
||||
let faucet_program = Program::faucet();
|
||||
let pinata_program = Program::pinata();
|
||||
|
||||
assert_eq!(auth_transfer_program.id, AUTHENTICATED_TRANSFER_ID);
|
||||
@ -509,6 +519,8 @@ mod tests {
|
||||
assert_eq!(token_program.elf, TOKEN_ELF);
|
||||
assert_eq!(vault_program.id, VAULT_ID);
|
||||
assert_eq!(vault_program.elf, VAULT_ELF);
|
||||
assert_eq!(faucet_program.id, FAUCET_ID);
|
||||
assert_eq!(faucet_program.elf, FAUCET_ELF);
|
||||
assert_eq!(pinata_program.id, PINATA_ID);
|
||||
assert_eq!(pinata_program.elf, PINATA_ELF);
|
||||
}
|
||||
@ -520,6 +532,7 @@ mod tests {
|
||||
(AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID),
|
||||
(ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID),
|
||||
(CLOCK_ELF, CLOCK_ID),
|
||||
(FAUCET_ELF, FAUCET_ID),
|
||||
(PINATA_ELF, PINATA_ID),
|
||||
(PINATA_TOKEN_ELF, PINATA_TOKEN_ID),
|
||||
(TOKEN_ELF, TOKEN_ID),
|
||||
|
||||
@ -8,7 +8,7 @@ pub use clock_core::{
|
||||
};
|
||||
use nssa_core::{
|
||||
BlockId, Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
||||
SYSTEM_FAUCET_ACCOUNT_ID, Timestamp,
|
||||
Timestamp,
|
||||
account::{Account, AccountId, Nonce},
|
||||
program::ProgramId,
|
||||
};
|
||||
@ -124,9 +124,10 @@ pub struct V03State {
|
||||
|
||||
impl Default for V03State {
|
||||
fn default() -> Self {
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
let faucet_account = system_faucet_account();
|
||||
let mut public_state = HashMap::new();
|
||||
public_state.insert(SYSTEM_FAUCET_ACCOUNT_ID, faucet_account);
|
||||
public_state.insert(faucet_account_id, faucet_account);
|
||||
|
||||
Self {
|
||||
public_state,
|
||||
@ -148,6 +149,7 @@ impl V03State {
|
||||
initial_private_accounts: Vec<(Commitment, Nullifier)>,
|
||||
genesis_timestamp: nssa_core::Timestamp,
|
||||
) -> Self {
|
||||
let faucet_account_id = system_faucet_account_id();
|
||||
let authenticated_transfer_program = Program::authenticated_transfer_program();
|
||||
let mut public_state: HashMap<_, _> = initial_data
|
||||
.iter()
|
||||
@ -162,7 +164,7 @@ impl V03State {
|
||||
})
|
||||
.collect();
|
||||
let faucet_account = system_faucet_account();
|
||||
public_state.insert(SYSTEM_FAUCET_ACCOUNT_ID, faucet_account);
|
||||
public_state.insert(faucet_account_id, faucet_account);
|
||||
|
||||
let mut commitment_set = CommitmentSet::with_capacity(32);
|
||||
commitment_set.extend(&[DUMMY_COMMITMENT]);
|
||||
@ -187,6 +189,7 @@ impl V03State {
|
||||
this.insert_program(Program::amm());
|
||||
this.insert_program(Program::ata());
|
||||
this.insert_program(Program::vault());
|
||||
this.insert_program(Program::faucet());
|
||||
|
||||
this
|
||||
}
|
||||
@ -381,6 +384,11 @@ fn system_faucet_account() -> Account {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn system_faucet_account_id() -> AccountId {
|
||||
faucet_core::compute_faucet_account_id(Program::faucet().id())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
#![expect(
|
||||
@ -404,7 +412,7 @@ pub mod tests {
|
||||
};
|
||||
|
||||
use crate::{
|
||||
PublicKey, PublicTransaction, SYSTEM_FAUCET_ACCOUNT_ID, V03State,
|
||||
PublicKey, PublicTransaction, V03State,
|
||||
error::{InvalidProgramBehaviorError, NssaError},
|
||||
execute_and_prove,
|
||||
privacy_preserving_transaction::{
|
||||
@ -420,6 +428,7 @@ pub mod tests {
|
||||
CLOCK_01_PROGRAM_ACCOUNT_ID, CLOCK_10_PROGRAM_ACCOUNT_ID, CLOCK_50_PROGRAM_ACCOUNT_ID,
|
||||
CLOCK_PROGRAM_ACCOUNT_IDS, MAX_NUMBER_CHAINED_CALLS, system_faucet_account,
|
||||
},
|
||||
system_faucet_account_id,
|
||||
};
|
||||
|
||||
impl V03State {
|
||||
@ -612,7 +621,7 @@ pub mod tests {
|
||||
..Account::default()
|
||||
},
|
||||
);
|
||||
this.insert(SYSTEM_FAUCET_ACCOUNT_ID, system_faucet_account());
|
||||
this.insert(system_faucet_account_id(), system_faucet_account());
|
||||
for account_id in CLOCK_PROGRAM_ACCOUNT_IDS {
|
||||
this.insert(
|
||||
account_id,
|
||||
@ -636,6 +645,7 @@ pub mod tests {
|
||||
this.insert(Program::amm().id(), Program::amm());
|
||||
this.insert(Program::ata().id(), Program::ata());
|
||||
this.insert(Program::vault().id(), Program::vault());
|
||||
this.insert(Program::faucet().id(), Program::faucet());
|
||||
this
|
||||
};
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ amm_core.workspace = true
|
||||
amm_program.workspace = true
|
||||
ata_core.workspace = true
|
||||
ata_program.workspace = true
|
||||
faucet_core.workspace = true
|
||||
vault_core.workspace = true
|
||||
risc0-zkvm.workspace = true
|
||||
serde = { workspace = true, default-features = false }
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use authenticated_transfer_core::Instruction;
|
||||
use nssa_core::{
|
||||
SYSTEM_FAUCET_ACCOUNT_ID,
|
||||
account::{Account, AccountWithMetadata},
|
||||
program::{
|
||||
AccountPostState, Claim, DEFAULT_PROGRAM_ID, ProgramInput, ProgramOutput, read_nssa_inputs,
|
||||
@ -26,13 +25,8 @@ fn transfer(
|
||||
recipient: AccountWithMetadata,
|
||||
balance_to_move: u128,
|
||||
) -> Vec<AccountPostState> {
|
||||
// Continue only if the sender has authorized this operation
|
||||
// or it's the system faucet account which is allowed without authorization as it may be used
|
||||
// only by sequencer.
|
||||
assert!(
|
||||
sender.is_authorized || sender.account_id == SYSTEM_FAUCET_ACCOUNT_ID,
|
||||
"Sender must be authorized"
|
||||
);
|
||||
// Continue only if the sender has authorized this operation.
|
||||
assert!(sender.is_authorized, "Sender must be authorized");
|
||||
|
||||
// Create accounts post states, with updated balances
|
||||
let sender_post = {
|
||||
|
||||
71
program_methods/guest/src/bin/faucet.rs
Normal file
71
program_methods/guest/src/bin/faucet.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use faucet_core::Instruction;
|
||||
use nssa_core::program::{
|
||||
AccountPostState, ChainedCall, ProgramInput, ProgramOutput, read_nssa_inputs,
|
||||
};
|
||||
|
||||
fn unchanged_post_states(
|
||||
pre_states: &[nssa_core::account::AccountWithMetadata],
|
||||
) -> Vec<AccountPostState> {
|
||||
pre_states
|
||||
.iter()
|
||||
.map(|pre_state| AccountPostState::new(pre_state.account.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (
|
||||
ProgramInput {
|
||||
self_program_id,
|
||||
caller_program_id,
|
||||
pre_states,
|
||||
instruction,
|
||||
},
|
||||
instruction_words,
|
||||
) = read_nssa_inputs::<Instruction>();
|
||||
|
||||
let pre_states_clone = pre_states.clone();
|
||||
let post_states = unchanged_post_states(&pre_states_clone);
|
||||
|
||||
let chained_calls = match instruction {
|
||||
Instruction::Transfer {
|
||||
vault_program_id,
|
||||
recipient_id,
|
||||
amount,
|
||||
} => {
|
||||
let [faucet, recipient_vault] = pre_states
|
||||
.try_into()
|
||||
.expect("Transfer requires exactly 2 accounts");
|
||||
|
||||
assert_eq!(
|
||||
faucet.account_id,
|
||||
faucet_core::compute_faucet_account_id(self_program_id),
|
||||
"First account must be faucet PDA"
|
||||
);
|
||||
|
||||
let mut faucet_for_vault = faucet;
|
||||
faucet_for_vault.is_authorized = true;
|
||||
|
||||
vec![
|
||||
ChainedCall::new(
|
||||
vault_program_id,
|
||||
vec![faucet_for_vault, recipient_vault],
|
||||
&vault_core::Instruction::Transfer {
|
||||
recipient_id,
|
||||
amount,
|
||||
},
|
||||
)
|
||||
.with_pda_seeds(vec![faucet_core::compute_faucet_seed()]),
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
ProgramOutput::new(
|
||||
self_program_id,
|
||||
caller_program_id,
|
||||
instruction_words,
|
||||
pre_states_clone,
|
||||
post_states,
|
||||
)
|
||||
.with_chained_calls(chained_calls)
|
||||
.write();
|
||||
}
|
||||
12
programs/faucet/core/Cargo.toml
Normal file
12
programs/faucet/core/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "faucet_core"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nssa_core.workspace = true
|
||||
serde = { workspace = true, default-features = false }
|
||||
29
programs/faucet/core/src/lib.rs
Normal file
29
programs/faucet/core/src/lib.rs
Normal file
@ -0,0 +1,29 @@
|
||||
pub use nssa_core::program::PdaSeed;
|
||||
use nssa_core::{account::AccountId, program::ProgramId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const FAUCET_SEED_DOMAIN_SEPARATOR: [u8; 32] = *b"/LEZ/v0.3/FaucetSeed/0000000000/";
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum Instruction {
|
||||
/// Transfers native tokens from system faucet to recipient's vault.
|
||||
///
|
||||
/// Required accounts (2):
|
||||
/// - Faucet PDA account
|
||||
/// - Recipient vault PDA account
|
||||
Transfer {
|
||||
vault_program_id: ProgramId,
|
||||
recipient_id: AccountId,
|
||||
amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn compute_faucet_seed() -> PdaSeed {
|
||||
PdaSeed::new(FAUCET_SEED_DOMAIN_SEPARATOR)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn compute_faucet_account_id(faucet_program_id: ProgramId) -> AccountId {
|
||||
AccountId::for_public_pda(&faucet_program_id, &compute_faucet_seed())
|
||||
}
|
||||
@ -15,6 +15,7 @@ storage.workspace = true
|
||||
mempool.workspace = true
|
||||
logos-blockchain-zone-sdk.workspace = true
|
||||
testnet_initial_state.workspace = true
|
||||
faucet_core.workspace = true
|
||||
vault_core.workspace = true
|
||||
|
||||
thiserror.workspace = true
|
||||
|
||||
@ -383,14 +383,16 @@ fn build_supply_account_genesis_transaction(
|
||||
account_id: &AccountId,
|
||||
balance: u128,
|
||||
) -> PublicTransaction {
|
||||
let faucet_program_id = Program::faucet().id();
|
||||
let vault_program_id = Program::vault().id();
|
||||
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, *account_id);
|
||||
|
||||
let message = Message::try_new(
|
||||
vault_program_id,
|
||||
vec![nssa::SYSTEM_FAUCET_ACCOUNT_ID, recipient_vault_id],
|
||||
faucet_program_id,
|
||||
vec![nssa::system_faucet_account_id(), recipient_vault_id],
|
||||
vec![],
|
||||
vault_core::Instruction::Transfer {
|
||||
faucet_core::Instruction::Transfer {
|
||||
vault_program_id,
|
||||
recipient_id: *account_id,
|
||||
amount: balance,
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user