use steel::*;
use crate::{
consts::{
BASIS_POINTS_DENOMINATOR, DEFAULT_FEE_BPS, PAYMENT_STATE_FUNDED, PAYMENT_STATE_REFUNDED,
PAYMENT_STATE_RELEASED,
},
error::EscrowError,
instruction::*,
state::{bank_pda, escrow_pda, payment_pda, sol_storage_pda, Payment},
};
pub struct EscrowSdk;
impl EscrowSdk {
pub fn program_id() -> Pubkey {
crate::ID
}
pub fn bank_pda() -> (Pubkey, u8) {
bank_pda()
}
pub fn config_pda() -> (Pubkey, u8) {
crate::state::config_pda()
}
pub fn escrow_pda(mint: Pubkey) -> (Pubkey, u8) {
let (bank_pda, _) = Self::bank_pda();
escrow_pda(mint, bank_pda)
}
pub fn payment_pda(payment_uid: &str, bank: Pubkey) -> (Pubkey, u8) {
payment_pda(payment_uid, bank)
}
pub fn sol_storage_pda(mint: Pubkey, bank: Pubkey, escrow: Pubkey) -> (Pubkey, u8) {
sol_storage_pda(mint, bank, escrow)
}
pub fn associated_token_account(wallet: Pubkey, mint: Pubkey) -> Pubkey {
spl_associated_token_account::get_associated_token_address_with_program_id(
&wallet,
&mint,
&spl_token::ID,
)
}
pub fn escrow_token_account(mint: Pubkey) -> Pubkey {
let (escrow_pda, _) = Self::escrow_pda(mint);
spl_associated_token_account::get_associated_token_address_with_program_id(
&escrow_pda,
&mint,
&spl_token::ID,
)
}
pub fn calculate_gross_quote(
desired_net: u64,
fee_bps: u16,
min_fee_amount: u64,
oracle_fee_bps: u16,
) -> u64 {
let numerator_a =
(desired_net as u128 + min_fee_amount as u128).saturating_mul(BASIS_POINTS_DENOMINATOR);
let denominator_a = (BASIS_POINTS_DENOMINATOR - oracle_fee_bps as u128).max(1);
let amount_a = (numerator_a.saturating_add(denominator_a - 1) / denominator_a) as u64;
let numerator_b = (desired_net as u128).saturating_mul(BASIS_POINTS_DENOMINATOR);
let denominator_b =
(BASIS_POINTS_DENOMINATOR - fee_bps as u128 - oracle_fee_bps as u128).max(1);
let amount_b = (numerator_b.saturating_add(denominator_b - 1) / denominator_b) as u64;
amount_a.max(amount_b)
}
pub fn initialize(signer: Pubkey, fee_bps: Option<u16>) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(signer, true),
AccountMeta::new(bank_pda, false),
AccountMeta::new(config_pda, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(solana_program::sysvar::rent::ID, false),
],
data: Initialize {
fee_bps: (fee_bps.unwrap_or(DEFAULT_FEE_BPS)).to_le_bytes(),
_padding: [0; 6], }
.to_bytes(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn open_escrow(
signer: Pubkey,
payer: Pubkey,
mint: Pubkey,
min_payment_amount: u64,
max_payment_amount: u64,
min_fee_amount: u64,
fee_bps: u16,
oracle_fee_bps: u16,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let escrow_tokens = if mint == Pubkey::default() {
escrow_pda
} else {
Self::escrow_token_account(mint)
};
let mut accounts = vec![
AccountMeta::new(signer, true),
AccountMeta::new(payer, true),
AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new(escrow_pda, false),
AccountMeta::new(escrow_tokens, false),
AccountMeta::new_readonly(mint, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new_readonly(spl_associated_token_account::ID, false),
AccountMeta::new_readonly(solana_program::sysvar::rent::ID, false),
];
if mint == Pubkey::default() {
let (sol_storage_pda, _) = Self::sol_storage_pda(mint, bank_pda, escrow_pda);
accounts.push(AccountMeta::new(sol_storage_pda, false));
}
Instruction {
program_id: Self::program_id(),
accounts,
data: OpenEscrow {
min_payment_amount: min_payment_amount.to_le_bytes(),
max_payment_amount: max_payment_amount.to_le_bytes(),
min_fee_amount: min_fee_amount.to_le_bytes(),
fee_bps: fee_bps.to_le_bytes(),
oracle_fee_bps: oracle_fee_bps.to_le_bytes(),
_padding: [0; 4],
}
.to_bytes(),
}
}
fn sanitize_payment_uid(payment_uid: &str) -> [u8; 32] {
let mut uid_bytes = [0u8; 32];
let uid_str = payment_uid.replace('-', "");
let uid_bytes_str = uid_str.as_bytes();
let len = uid_bytes_str.len().min(32);
uid_bytes[..len].copy_from_slice(&uid_bytes_str[..len]);
uid_bytes
}
#[allow(clippy::too_many_arguments)]
pub fn fund_payment(
buyer: Pubkey,
buyer_tokens: Option<Pubkey>, seller: Pubkey,
mint: Pubkey,
amount: u64,
ttl_seconds: i64,
payment_uid: &str,
sla_hash: [u8; 32],
oracle_authority: Pubkey,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let (payment_pda, _) = crate::state::payment_pda(payment_uid, bank_pda);
let is_sol = mint == Pubkey::default();
let mut accounts = vec![
AccountMeta::new(buyer, true),
AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(escrow_pda, false),
AccountMeta::new(payment_pda, false),
AccountMeta::new_readonly(mint, false),
];
if is_sol {
let (sol_storage_pda, _) = Self::sol_storage_pda(mint, bank_pda, escrow_pda);
accounts.push(AccountMeta::new(sol_storage_pda, false));
accounts.push(AccountMeta::new_readonly(
solana_program::system_program::ID,
false,
));
} else {
let buyer_tokens = buyer_tokens.expect("buyer_tokens required for SPL tokens");
let escrow_tokens = Self::escrow_token_account(mint);
accounts.push(AccountMeta::new(escrow_tokens, false));
accounts.push(AccountMeta::new(buyer_tokens, false));
accounts.push(AccountMeta::new_readonly(spl_token::ID, false));
accounts.push(AccountMeta::new_readonly(
solana_program::system_program::ID,
false,
));
}
Instruction {
program_id: Self::program_id(),
accounts,
data: FundPayment {
seller,
mint,
oracle_authority,
payment_uid: Self::sanitize_payment_uid(payment_uid),
sla_hash,
amount: amount.to_le_bytes(),
ttl_seconds: ttl_seconds.to_le_bytes(),
}
.to_bytes(),
}
}
pub fn release_payment(
caller: Pubkey,
seller_tokens: Option<Pubkey>, seller: Option<Pubkey>, mint: Pubkey,
payment_uid: &str,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let (payment_pda, _) = Self::payment_pda(payment_uid, bank_pda);
let is_sol = mint == Pubkey::default();
let mut accounts = vec![
AccountMeta::new(caller, true),
AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(escrow_pda, false),
AccountMeta::new(payment_pda, false),
AccountMeta::new_readonly(mint, false),
];
if is_sol {
let (sol_storage_pda, _) = Self::sol_storage_pda(mint, bank_pda, escrow_pda);
accounts.push(AccountMeta::new(sol_storage_pda, false));
accounts.push(AccountMeta::new(
seller.expect("seller required for SOL"),
false,
));
accounts.push(AccountMeta::new_readonly(
solana_program::system_program::ID,
false,
));
} else {
let seller_tokens = seller_tokens.expect("seller_tokens required for SPL tokens");
let seller = seller.expect("seller account required for SPL tokens");
let escrow_tokens = Self::escrow_token_account(mint);
accounts.push(AccountMeta::new(escrow_tokens, false));
accounts.push(AccountMeta::new(seller_tokens, false));
accounts.push(AccountMeta::new(seller, false));
accounts.push(AccountMeta::new_readonly(spl_token::ID, false));
accounts.push(AccountMeta::new_readonly(
spl_associated_token_account::ID,
false,
));
accounts.push(AccountMeta::new_readonly(
solana_program::system_program::ID,
false,
));
}
Instruction {
program_id: Self::program_id(),
accounts,
data: ReleasePayment {}.to_bytes(),
}
}
pub fn refund_payment(
caller: Pubkey,
buyer_tokens: Option<Pubkey>, mint: Pubkey,
payment_uid: &str,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let (payment_pda, _) = Self::payment_pda(payment_uid, bank_pda);
let is_sol = mint == Pubkey::default();
let mut accounts = vec![
AccountMeta::new(caller, true),
AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(escrow_pda, false),
AccountMeta::new(payment_pda, false),
AccountMeta::new_readonly(mint, false),
];
if is_sol {
let (sol_storage_pda, _) = Self::sol_storage_pda(mint, bank_pda, escrow_pda);
accounts.push(AccountMeta::new(sol_storage_pda, false));
accounts.push(AccountMeta::new(
buyer_tokens.expect("buyer required for SOL"),
false,
));
accounts.push(AccountMeta::new_readonly(
solana_program::system_program::ID,
false,
));
} else {
let buyer_tokens = buyer_tokens.expect("buyer_tokens required for SPL tokens");
let escrow_tokens = Self::escrow_token_account(mint);
accounts.push(AccountMeta::new(escrow_tokens, false));
accounts.push(AccountMeta::new(buyer_tokens, false));
accounts.push(AccountMeta::new_readonly(spl_token::ID, false));
}
Instruction {
program_id: Self::program_id(),
accounts,
data: RefundPayment {}.to_bytes(),
}
}
pub fn submit_delivery(
caller: Pubkey,
mint: Pubkey,
payment_uid: &str,
delivery_hash: [u8; 32],
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let (payment_pda, _) = Self::payment_pda(payment_uid, bank_pda);
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(caller, true), AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new_readonly(escrow_pda, false),
AccountMeta::new(payment_pda, false),
],
data: SubmitDelivery { delivery_hash }.to_bytes(),
}
}
pub fn close_payment(
caller: Pubkey,
buyer: Pubkey,
mint: Pubkey,
payment_uid: &str,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let (payment_pda, _) = Self::payment_pda(payment_uid, bank_pda);
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(caller, true), AccountMeta::new(buyer, false), AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(escrow_pda, false),
AccountMeta::new(payment_pda, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
],
data: ClosePayment {}.to_bytes(),
}
}
pub fn withdraw_fees(
authority: Pubkey,
beneficiary: Pubkey,
mint: Pubkey,
amount: u64,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let is_sol = mint == Pubkey::default();
let mut accounts = vec![
AccountMeta::new(authority, true),
AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(escrow_pda, false),
AccountMeta::new(beneficiary, false),
];
if is_sol {
let (sol_storage_pda, _) = Self::sol_storage_pda(mint, bank_pda, escrow_pda);
accounts.push(AccountMeta::new(sol_storage_pda, false));
accounts.push(AccountMeta::new_readonly(
solana_program::system_program::ID,
false,
));
} else {
let escrow_tokens = Self::escrow_token_account(mint);
let beneficiary_tokens = Self::associated_token_account(beneficiary, mint);
accounts[4] = AccountMeta::new(escrow_tokens, false);
accounts.push(AccountMeta::new(beneficiary_tokens, false));
accounts.push(AccountMeta::new_readonly(mint, false));
accounts.push(AccountMeta::new_readonly(spl_token::ID, false));
}
Instruction {
program_id: Self::program_id(),
accounts,
data: WithdrawFees {
amount: amount.to_le_bytes(),
}
.to_bytes(),
}
}
pub fn extend_payment_ttl(
caller: Pubkey,
mint: Pubkey,
payment_uid: &str,
additional_seconds: i64,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let (payment_pda, _) = Self::payment_pda(payment_uid, bank_pda);
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(caller, true),
AccountMeta::new_readonly(bank_pda, false), AccountMeta::new_readonly(config_pda, false),
AccountMeta::new_readonly(escrow_pda, false),
AccountMeta::new(payment_pda, false), AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(solana_program::sysvar::rent::ID, false),
],
data: ExtendPaymentTTL {
additional_seconds: additional_seconds.to_le_bytes(),
}
.to_bytes(),
}
}
pub fn close_escrow(authority: Pubkey, recipient: Pubkey, mint: Pubkey) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let escrow_tokens = if mint == Pubkey::default() {
let (sol_storage_pda, _) = Self::sol_storage_pda(mint, bank_pda, escrow_pda);
sol_storage_pda
} else {
Self::escrow_token_account(mint)
};
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new(escrow_pda, false),
AccountMeta::new(escrow_tokens, false),
AccountMeta::new_readonly(mint, false),
AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new(recipient, false),
],
data: CloseEscrow {}.to_bytes(),
}
}
pub fn update_escrow_settings(
authority: Pubkey,
escrow: Pubkey,
fee_bps: u16,
min_payment_amount: u64,
max_payment_amount: u64,
min_fee_amount: u64,
oracle_fee_bps: u16,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new_readonly(bank_pda, false), AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(escrow, false), AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(solana_program::sysvar::rent::ID, false),
],
data: UpdateEscrowSettings {
min_payment_amount: min_payment_amount.to_le_bytes(),
max_payment_amount: max_payment_amount.to_le_bytes(),
min_fee_amount: min_fee_amount.to_le_bytes(),
new_fee_bps: fee_bps.to_le_bytes(),
new_oracle_fee_bps: oracle_fee_bps.to_le_bytes(),
_padding: [0; 4],
}
.to_bytes(),
}
}
pub fn pause_escrow(authority: Pubkey, mint: Pubkey, pause: bool) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(authority, true),
AccountMeta::new_readonly(bank_pda, false), AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(escrow_pda, false), AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(solana_program::sysvar::rent::ID, false),
],
data: PauseEscrow {
pause: if pause { 1 } else { 0 },
}
.to_bytes(),
}
}
pub fn update_authority(current_authority: Pubkey, new_authority: Pubkey) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(current_authority, true),
AccountMeta::new(bank_pda, false),
],
data: UpdateAuthority { new_authority }.to_bytes(),
}
}
pub fn confirm_oracle(
oracle_authority: Pubkey,
mint: Pubkey,
payment_uid: &str,
delivery_hash: [u8; 32],
resolution_state: u8,
) -> Instruction {
let (bank_pda, _) = Self::bank_pda();
let (config_pda, _) = Self::config_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let (payment_pda, _) = Self::payment_pda(payment_uid, bank_pda);
Instruction {
program_id: Self::program_id(),
accounts: vec![
AccountMeta::new(oracle_authority, true), AccountMeta::new_readonly(bank_pda, false),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new_readonly(escrow_pda, false),
AccountMeta::new(payment_pda, false), ],
data: ConfirmOracle {
delivery_hash,
resolution_state,
_padding: [0; 7],
}
.to_bytes(),
}
}
pub fn validate_payment_for_funding(payment: &Payment) -> Result<(), EscrowError> {
if payment.state != PAYMENT_STATE_FUNDED {
return Err(EscrowError::InvalidPaymentState);
}
Ok(())
}
pub fn validate_payment_for_release(payment: &Payment) -> Result<(), EscrowError> {
if payment.state != PAYMENT_STATE_FUNDED {
return Err(EscrowError::InvalidPaymentState);
}
Ok(())
}
pub fn validate_payment_for_refund(payment: &Payment) -> Result<(), EscrowError> {
if payment.state != PAYMENT_STATE_FUNDED {
return Err(EscrowError::InvalidPaymentState);
}
Ok(())
}
pub fn calculate_fee(amount: u64, fee_bps: u16, min_fee_amount: u64) -> u64 {
let fee_bps = fee_bps as u128;
let amount_128 = amount as u128;
let calculated_fee = ((amount_128 * fee_bps) / BASIS_POINTS_DENOMINATOR) as u64;
calculated_fee.max(min_fee_amount).min(amount)
}
pub fn calculate_payout(amount: u64, fee_bps: u16, min_fee_amount: u64) -> u64 {
let fee = Self::calculate_fee(amount, fee_bps, min_fee_amount);
amount.saturating_sub(fee)
}
pub fn is_payment_expired(payment: &Payment) -> bool {
let clock = solana_program::clock::Clock::get().unwrap_or_default();
clock.unix_timestamp > payment.expires_at
}
pub fn get_payment_state_string(state: u8) -> &'static str {
match state {
PAYMENT_STATE_FUNDED => "Funded",
PAYMENT_STATE_RELEASED => "Released",
PAYMENT_STATE_REFUNDED => "Refunded",
_ => "Unknown",
}
}
pub fn get_escrow_state_string(paused: u8) -> &'static str {
match paused {
0 => "Active",
1 => "Paused",
_ => "Unknown",
}
}
pub fn get_payment_accounts(
buyer: Pubkey,
seller: Pubkey,
mint: Pubkey,
payment_uid: &str,
) -> PaymentAccounts {
let (bank_pda, _) = Self::bank_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let (payment_pda, _) = Self::payment_pda(payment_uid, bank_pda);
let escrow_tokens = Self::escrow_token_account(mint);
PaymentAccounts {
buyer,
seller,
mint,
bank: bank_pda,
escrow: escrow_pda,
payment: payment_pda,
escrow_tokens,
}
}
pub fn get_escrow_accounts(mint: Pubkey) -> EscrowAccounts {
let (bank_pda, _) = Self::bank_pda();
let (escrow_pda, _) = Self::escrow_pda(mint);
let escrow_tokens = Self::escrow_token_account(mint);
EscrowAccounts {
mint,
bank: bank_pda,
escrow: escrow_pda,
escrow_tokens,
}
}
}
#[derive(Debug, Clone)]
pub struct PaymentAccounts {
pub buyer: Pubkey,
pub seller: Pubkey,
pub mint: Pubkey,
pub bank: Pubkey,
pub escrow: Pubkey,
pub payment: Pubkey,
pub escrow_tokens: Pubkey,
}
#[derive(Debug, Clone)]
pub struct EscrowAccounts {
pub mint: Pubkey,
pub bank: Pubkey,
pub escrow: Pubkey,
pub escrow_tokens: Pubkey,
}
pub struct PaymentBuilder {
buyer: Option<Pubkey>,
buyer_tokens: Option<Pubkey>,
seller: Option<Pubkey>,
mint: Option<Pubkey>,
amount: Option<u64>,
ttl_seconds: Option<i64>,
payment_uid: Option<String>,
sla_hash: Option<[u8; 32]>,
oracle_authority: Option<Pubkey>,
}
impl PaymentBuilder {
pub fn new() -> Self {
Self {
buyer: None,
buyer_tokens: None,
seller: None,
mint: None,
amount: None,
ttl_seconds: None,
payment_uid: None,
sla_hash: None,
oracle_authority: None,
}
}
pub fn buyer(mut self, buyer: Pubkey) -> Self {
self.buyer = Some(buyer);
self
}
pub fn buyer_tokens(mut self, buyer_tokens: Pubkey) -> Self {
self.buyer_tokens = Some(buyer_tokens);
self
}
pub fn seller(mut self, seller: Pubkey) -> Self {
self.seller = Some(seller);
self
}
pub fn mint(mut self, mint: Pubkey) -> Self {
self.mint = Some(mint);
self
}
pub fn amount(mut self, amount: u64) -> Self {
self.amount = Some(amount);
self
}
pub fn ttl_seconds(mut self, ttl_seconds: i64) -> Self {
self.ttl_seconds = Some(ttl_seconds);
self
}
pub fn payment_uid(mut self, payment_uid: &str) -> Self {
self.payment_uid = Some(payment_uid.to_string());
self
}
pub fn sla_hash(mut self, sla_hash: [u8; 32]) -> Self {
self.sla_hash = Some(sla_hash);
self
}
pub fn oracle_authority(mut self, oracle_authority: Pubkey) -> Self {
self.oracle_authority = Some(oracle_authority);
self
}
pub fn build(self) -> Result<Instruction, EscrowError> {
let buyer = self.buyer.ok_or(EscrowError::MissingRequiredField)?;
let buyer_tokens = self.buyer_tokens; let seller = self.seller.ok_or(EscrowError::MissingRequiredField)?;
let mint = self.mint.ok_or(EscrowError::MissingRequiredField)?;
let amount = self.amount.ok_or(EscrowError::MissingRequiredField)?;
let ttl_seconds = self.ttl_seconds.ok_or(EscrowError::MissingRequiredField)?;
let payment_uid = self.payment_uid.ok_or(EscrowError::MissingRequiredField)?;
let sla_hash = self.sla_hash.unwrap_or([0; 32]);
let oracle_authority = self.oracle_authority.unwrap_or_default();
Ok(EscrowSdk::fund_payment(
buyer,
buyer_tokens,
seller,
mint,
amount,
ttl_seconds,
&payment_uid,
sla_hash,
oracle_authority,
))
}
}
impl Default for PaymentBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct EscrowBuilder {
authority: Option<Pubkey>,
funder: Option<Pubkey>,
mint: Option<Pubkey>,
min_payment_amount: Option<u64>,
max_payment_amount: Option<u64>,
min_fee_amount: Option<u64>,
fee_bps: Option<u16>,
oracle_fee_bps: Option<u16>,
}
impl EscrowBuilder {
pub fn new() -> Self {
Self {
authority: None,
funder: None,
mint: None,
min_payment_amount: None,
max_payment_amount: None,
min_fee_amount: None,
fee_bps: None,
oracle_fee_bps: None,
}
}
pub fn authority(mut self, authority: Pubkey) -> Self {
self.authority = Some(authority);
self
}
pub fn funder(mut self, funder: Pubkey) -> Self {
self.funder = Some(funder);
self
}
pub fn mint(mut self, mint: Pubkey) -> Self {
self.mint = Some(mint);
self
}
pub fn min_payment_amount(mut self, min_payment_amount: u64) -> Self {
self.min_payment_amount = Some(min_payment_amount);
self
}
pub fn max_payment_amount(mut self, max_payment_amount: u64) -> Self {
self.max_payment_amount = Some(max_payment_amount);
self
}
pub fn min_fee_amount(mut self, min_fee_amount: u64) -> Self {
self.min_fee_amount = Some(min_fee_amount);
self
}
pub fn fee_bps(mut self, fee_bps: u16) -> Self {
self.fee_bps = Some(fee_bps);
self
}
pub fn oracle_fee_bps(mut self, oracle_fee_bps: u16) -> Self {
self.oracle_fee_bps = Some(oracle_fee_bps);
self
}
pub fn create_escrow(self) -> Result<Instruction, EscrowError> {
let authority = self.authority.ok_or(EscrowError::MissingRequiredField)?;
let funder = self.funder.ok_or(EscrowError::MissingRequiredField)?;
let mint = self.mint.ok_or(EscrowError::MissingRequiredField)?;
let min_payment_amount = self
.min_payment_amount
.unwrap_or(crate::consts::DEFAULT_MIN_PAYMENT_RAW_USDC_DECIMALS_6);
let max_payment_amount = self.max_payment_amount.unwrap_or(u64::MAX); let min_fee_amount = self
.min_fee_amount
.unwrap_or(crate::consts::DEFAULT_MIN_FEE_RAW_USDC_DECIMALS_6);
let fee_bps = self.fee_bps.unwrap_or(DEFAULT_FEE_BPS);
let oracle_fee_bps = self.oracle_fee_bps.unwrap_or(0);
Ok(EscrowSdk::open_escrow(
authority,
funder,
mint,
min_payment_amount,
max_payment_amount,
min_fee_amount,
fee_bps,
oracle_fee_bps,
))
}
pub fn pause_escrow(self, paused: bool) -> Result<Instruction, EscrowError> {
let authority = self.authority.ok_or(EscrowError::MissingRequiredField)?;
let mint = self.mint.ok_or(EscrowError::MissingRequiredField)?;
Ok(EscrowSdk::pause_escrow(authority, mint, paused))
}
}
impl Default for EscrowBuilder {
fn default() -> Self {
Self::new()
}
}
pub mod tokens {
use solana_program::pubkey::Pubkey;
pub const USDC: Pubkey =
solana_program::pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
pub const USDT: Pubkey =
solana_program::pubkey!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB");
pub const WSOL: Pubkey = solana_program::pubkey!("So11111111111111111111111111111111111111112");
pub const MARS: Pubkey =
solana_program::pubkey!("7RAV5UPRTzxn46kLeA8MiJsdNy9VKc5fip8FWEgTpTHh");
pub const MIRACLE: Pubkey =
solana_program::pubkey!("Mirab4SFVff6sCuK48PPnSUj7PNpDDrBWY6FkJmuifG");
pub const TESTCOIN: Pubkey =
solana_program::pubkey!("2gNCDGj8Xi9Zs7LNQTPWf4pfZvAM7UHusY4xhKNYg6W6");
}
pub mod payment_states {
use crate::consts::{PAYMENT_STATE_FUNDED, PAYMENT_STATE_REFUNDED, PAYMENT_STATE_RELEASED};
pub const FUNDED: u8 = PAYMENT_STATE_FUNDED;
pub const RELEASED: u8 = PAYMENT_STATE_RELEASED;
pub const REFUNDED: u8 = PAYMENT_STATE_REFUNDED;
}
pub mod escrow_states {
pub const ACTIVE: u8 = 0;
pub const PAUSED: u8 = 1;
}