316 lines
10 KiB
Rust
316 lines
10 KiB
Rust
#![allow(dead_code, unused_imports)]
|
|
|
|
pub use anchor_lang::prelude::Pubkey;
|
|
pub use descro::{EscrowState, Winner as EscrowWinner};
|
|
use {
|
|
anchor_lang::{
|
|
solana_program::{instruction::Instruction, system_program},
|
|
AccountDeserialize, InstructionData, ToAccountMetas,
|
|
},
|
|
descro::{EscrowAccount, Winner},
|
|
litesvm::LiteSVM,
|
|
solana_keypair::Keypair,
|
|
solana_message::{Message, VersionedMessage},
|
|
solana_signer::Signer,
|
|
solana_transaction::versioned::VersionedTransaction,
|
|
};
|
|
|
|
pub const AMOUNT: u64 = 1_000_000_000;
|
|
pub const ESCROW_ID: u64 = 1;
|
|
|
|
pub fn setup() -> (LiteSVM, Keypair, Keypair, Keypair) {
|
|
let program_id = descro::id();
|
|
let mut svm = LiteSVM::new();
|
|
let bytes = include_bytes!("../../../../target/deploy/descro.so");
|
|
svm.add_program(program_id, bytes).unwrap();
|
|
|
|
let seller = Keypair::new();
|
|
let buyer = Keypair::new();
|
|
let resolver = Keypair::new();
|
|
|
|
svm.airdrop(&seller.pubkey(), 10_000_000_000).unwrap();
|
|
svm.airdrop(&buyer.pubkey(), 10_000_000_000).unwrap();
|
|
svm.airdrop(&resolver.pubkey(), 1_000_000_000).unwrap();
|
|
|
|
(svm, seller, buyer, resolver)
|
|
}
|
|
|
|
pub fn escrow_pda(seller: &Pubkey, escrow_id: u64) -> Pubkey {
|
|
Pubkey::find_program_address(
|
|
&[b"escrow", seller.as_ref(), &escrow_id.to_le_bytes()],
|
|
&descro::id(),
|
|
)
|
|
.0
|
|
}
|
|
|
|
pub fn vault_pda(escrow: &Pubkey) -> Pubkey {
|
|
Pubkey::find_program_address(&[b"vault", escrow.as_ref()], &descro::id()).0
|
|
}
|
|
|
|
pub fn escrow_authority_pda() -> Pubkey {
|
|
Pubkey::find_program_address(&[b"escrow_authority"], &descro::id()).0
|
|
}
|
|
|
|
pub fn resolver_entry_pda(resolver_authority: &Pubkey) -> Pubkey {
|
|
Pubkey::find_program_address(
|
|
&[b"resolver", resolver_authority.as_ref()],
|
|
&descro_ext_resolvers::id(),
|
|
)
|
|
.0
|
|
}
|
|
|
|
pub fn send(svm: &mut LiteSVM, ix: Instruction, payer: &Keypair) {
|
|
let blockhash = svm.latest_blockhash();
|
|
let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash);
|
|
let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[payer]).unwrap();
|
|
svm.send_transaction(tx).expect("transaction failed");
|
|
}
|
|
|
|
pub fn try_send(svm: &mut LiteSVM, ix: Instruction, payer: &Keypair) -> bool {
|
|
let blockhash = svm.latest_blockhash();
|
|
let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash);
|
|
let Ok(tx) = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[payer]) else {
|
|
return false;
|
|
};
|
|
svm.send_transaction(tx).is_ok()
|
|
}
|
|
|
|
pub fn ix_create_escrow(
|
|
seller: &Pubkey,
|
|
buyer: &Pubkey,
|
|
amount: u64,
|
|
dispute_resolver: Option<Pubkey>,
|
|
escrow_id: u64,
|
|
) -> Instruction {
|
|
// Default: derive the resolver_entry PDA from the resolver (won't exist → empty account → policy check skipped)
|
|
let dummy = Pubkey::new_from_array([1u8; 32]);
|
|
let (resolver, resolver_entry) = match dispute_resolver {
|
|
Some(r) => (r, resolver_entry_pda(&r)),
|
|
None => (dummy, dummy),
|
|
};
|
|
ix_create_escrow_with_resolver(seller, buyer, amount, dispute_resolver, escrow_id, &resolver, &resolver_entry)
|
|
}
|
|
|
|
pub fn ix_create_escrow_with_resolver(
|
|
seller: &Pubkey,
|
|
buyer: &Pubkey,
|
|
amount: u64,
|
|
dispute_resolver: Option<Pubkey>,
|
|
escrow_id: u64,
|
|
resolver: &Pubkey,
|
|
resolver_entry: &Pubkey,
|
|
) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
let vault = vault_pda(&escrow);
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::CreateEscrow {
|
|
amount,
|
|
dispute_resolver,
|
|
escrow_id,
|
|
}
|
|
.data(),
|
|
descro::accounts::CreateEscrow {
|
|
seller: *seller,
|
|
buyer: *buyer,
|
|
resolver: *resolver,
|
|
escrow_account: escrow,
|
|
vault,
|
|
resolver_entry: *resolver_entry,
|
|
system_program: system_program::ID,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|
|
pub fn ix_deposit(buyer: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
let vault = vault_pda(&escrow);
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::Deposit {}.data(),
|
|
descro::accounts::Deposit {
|
|
buyer: *buyer,
|
|
escrow_account: escrow,
|
|
vault,
|
|
system_program: system_program::ID,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|
|
pub fn ix_complete(buyer: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
let vault = vault_pda(&escrow);
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::Complete {}.data(),
|
|
descro::accounts::Complete {
|
|
buyer: *buyer,
|
|
seller: *seller,
|
|
escrow_account: escrow,
|
|
vault,
|
|
system_program: system_program::ID,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|
|
pub fn ix_dispute(initiator: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::Dispute {}.data(),
|
|
descro::accounts::Dispute {
|
|
initiator: *initiator,
|
|
escrow_account: escrow,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|
|
/// Builds a resolve instruction. Pass the actual registry program ID when testing with a
|
|
/// registered resolver; for unregistered resolvers pass `system_program::ID` as
|
|
/// `registry_program` (the handler skips the CPI if resolver_entry has no data).
|
|
pub fn ix_resolve(
|
|
resolver: &Pubkey,
|
|
winner_pubkey: &Pubkey,
|
|
seller: &Pubkey,
|
|
escrow_id: u64,
|
|
winner: Winner,
|
|
registry_program: Pubkey,
|
|
) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
let vault = vault_pda(&escrow);
|
|
let resolver_entry = resolver_entry_pda(resolver);
|
|
let escrow_authority = escrow_authority_pda();
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::Resolve { winner }.data(),
|
|
descro::accounts::Resolve {
|
|
resolver: *resolver,
|
|
winner: *winner_pubkey,
|
|
seller: *seller,
|
|
escrow_account: escrow,
|
|
vault,
|
|
resolver_entry,
|
|
escrow_authority,
|
|
registry_program,
|
|
system_program: system_program::ID,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|
|
pub fn ix_cancel(canceller: &Pubkey, buyer: &Pubkey, seller: &Pubkey, escrow_id: u64) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
let vault = vault_pda(&escrow);
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::Cancel {}.data(),
|
|
descro::accounts::Cancel {
|
|
canceller: *canceller,
|
|
buyer: *buyer,
|
|
escrow_account: escrow,
|
|
vault,
|
|
system_program: system_program::ID,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|
|
pub fn ix_buyer_create_escrow(
|
|
buyer: &Pubkey,
|
|
seller: &Pubkey,
|
|
amount: u64,
|
|
dispute_resolver: Option<Pubkey>,
|
|
escrow_id: u64,
|
|
) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
let vault = vault_pda(&escrow);
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::BuyerCreateEscrow {
|
|
amount,
|
|
dispute_resolver,
|
|
escrow_id,
|
|
}
|
|
.data(),
|
|
descro::accounts::BuyerCreateEscrow {
|
|
buyer: *buyer,
|
|
seller: *seller,
|
|
escrow_account: escrow,
|
|
vault,
|
|
system_program: system_program::ID,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|
|
pub fn ix_seller_confirm(seller: &Pubkey, resolver: &Pubkey, resolver_entry: &Pubkey, escrow_id: u64) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::SellerConfirm {}.data(),
|
|
descro::accounts::SellerConfirm {
|
|
seller: *seller,
|
|
resolver: *resolver,
|
|
escrow_account: escrow,
|
|
resolver_entry: *resolver_entry,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|
|
pub fn read_escrow(svm: &LiteSVM, seller: &Pubkey, escrow_id: u64) -> EscrowAccount {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
let account = svm.get_account(&escrow).expect("escrow account not found");
|
|
EscrowAccount::try_deserialize(&mut account.data.as_slice()).unwrap()
|
|
}
|
|
|
|
/// Send a transaction signed by multiple keypairs. `payer` is the fee payer and must be first.
|
|
pub fn send_multi(svm: &mut LiteSVM, ix: Instruction, payer: &Keypair, extra: &[&Keypair]) {
|
|
let blockhash = svm.latest_blockhash();
|
|
let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash);
|
|
let mut signers: Vec<&Keypair> = vec![payer];
|
|
signers.extend_from_slice(extra);
|
|
let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &signers).unwrap();
|
|
svm.send_transaction(tx).expect("multi-signer transaction failed");
|
|
}
|
|
|
|
pub fn try_send_multi(svm: &mut LiteSVM, ix: Instruction, payer: &Keypair, extra: &[&Keypair]) -> bool {
|
|
let blockhash = svm.latest_blockhash();
|
|
let msg = Message::new_with_blockhash(&[ix], Some(&payer.pubkey()), &blockhash);
|
|
let mut signers: Vec<&Keypair> = vec![payer];
|
|
signers.extend_from_slice(extra);
|
|
let Ok(tx) = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &signers) else {
|
|
return false;
|
|
};
|
|
svm.send_transaction(tx).is_ok()
|
|
}
|
|
|
|
pub fn ix_emergency_resolve(
|
|
buyer: &Pubkey,
|
|
seller: &Pubkey,
|
|
winner_pubkey: &Pubkey,
|
|
escrow_id: u64,
|
|
winner: Winner,
|
|
) -> Instruction {
|
|
let escrow = escrow_pda(seller, escrow_id);
|
|
let vault = vault_pda(&escrow);
|
|
Instruction::new_with_bytes(
|
|
descro::id(),
|
|
&descro::instruction::EmergencyResolve { winner }.data(),
|
|
descro::accounts::EmergencyResolve {
|
|
buyer: *buyer,
|
|
seller: *seller,
|
|
winner: *winner_pubkey,
|
|
escrow_account: escrow,
|
|
vault,
|
|
system_program: system_program::ID,
|
|
}
|
|
.to_account_metas(None),
|
|
)
|
|
}
|
|
|