Skip to content
4 changes: 3 additions & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ The program grant privileged roles to specific accounts (_authorities_). They ar
- Manager: the account that can update the address of all other roles.
- Solver Authority: the account that can add and remove solvers.
- Reclaim Authority: the account authorized to close buffer accounts, reclaim their rent, and choose where that rent goes.
- Settlement-Owned-Order Authority: the account authorized to place arbitrary orders that sell the protocol's own buffer balances (for fee withdrawals).
- Settlement-Owned-Order Authority: the account authorized to place arbitrary orders that sell the protocol's own buffer balances (for fee withdrawals), and to withdraw native SOL from the native SOL buffer.

### Updating authorities

Expand Down Expand Up @@ -92,6 +92,8 @@ Fees are withdrawn by placing an order, owned by the settlement state PDA, that

The order is placed through the `CreateSettlementOwnedOrder` instruction. The settlement-owned-order authority can specify arbitrary order parameters, as long as the owner is the state PDA.

Native SOL isn't a token, so it can't be sold through an order. The settlement-owned-order authority instead withdraws it directly from the native SOL buffer with the `WithdrawNativeSol` instruction, to a recipient of its choosing. The buffer's rent can't be withdrawn.

Differences with Ethereum:

- Solvers can't access the content of the buffers directly anymore (though they can do so indirectly by creating a dedicated order just to sweep the buffer).
Expand Down
2 changes: 1 addition & 1 deletion SECURITY_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ All roles, other than Solvers, are defined and manipulated using the same patter
| Manager | Ability to assign every role, its own included. Transfers take one step, with no acceptance or zero-address check |
| Solver authority | Adding and removing solvers, so it decides who can settle |
| Reclaim authority | Closing buffers and choosing where their rent goes. Can also burn tokens from the buffer to allow closing to happen |
| Settlement-owned-order authority | Ability to place orders that are owned by the settlement program, used to withdraw fees |
| Settlement-owned-order authority | Ability to place orders that are owned by the settlement program, used to withdraw fees, and to withdraw the native SOL buffer's lamports above rent |
| Solvers | Ability to settle user orders |

## Users
Expand Down
15 changes: 9 additions & 6 deletions bench-report.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"transfer_authority/manager_can_transfer_solver_authority": 4,
"transfer_authority/reclaim_authority_can_transfer_itself": 4,
"transfer_authority/settlement_owned_order_authority_can_transfer_itself": 4,
"transfer_authority/solver_authority_can_transfer_itself": 4
"transfer_authority/solver_authority_can_transfer_itself": 4,
"withdraw_native_sol/happy_path_withdraws_to_a_recipient_chosen_by_the_authority": 6
},
"compute_units": {
"add_solver/add_with_many_existing_solvers": 2061,
Expand All @@ -62,7 +63,7 @@
"create_buffers/max_buffers_in_one_instruction_token_2022": 224501,
"create_order/happy_path_creates_order_pda_with_expected_body": 6361,
"create_settlement_owned_order/places_an_order_owned_by_the_state_pda": 3441,
"initialize/happy_path_initializes_state_pda_with_expected_data": 7567,
"initialize/happy_path_initializes_state_pda_with_expected_data": 7566,
"reclaim_buffer/funded_buffer_within_limit_is_burned_and_closed": 7199,
"reclaim_buffer/funded_buffer_within_limit_is_burned_and_closed_token_2022": 10518,
"reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 6029,
Expand All @@ -73,8 +74,8 @@
"reclaim_order/happy_path_order_cancelled_is_reclaimable_by_owner_before_expiry": 2089,
"reclaim_order/happy_path_order_fully_filled_is_reclaimable_before_expiry": 1929,
"reclaim_order/on_chain_order_partially_filled_is_not_reclaimable_before_expiry": null,
"remove_solver/remove_with_many_existing_solvers": 754,
"remove_solver/removes_a_solver": 489,
"remove_solver/remove_with_many_existing_solvers": 755,
"remove_solver/removes_a_solver": 490,
"settle/finalizes_with_no_pushes": 1108,
"settle/happy_path_sell_tokens_for_native_sol": 4663,
"settle/pulls_from_multiple_orders": 13366,
Expand All @@ -95,7 +96,8 @@
"transfer_authority/manager_can_transfer_solver_authority": 178,
"transfer_authority/reclaim_authority_can_transfer_itself": 203,
"transfer_authority/settlement_owned_order_authority_can_transfer_itself": 201,
"transfer_authority/solver_authority_can_transfer_itself": 203
"transfer_authority/solver_authority_can_transfer_itself": 203,
"withdraw_native_sol/happy_path_withdraws_to_a_recipient_chosen_by_the_authority": 369
},
"transaction_bytes": {
"add_solver/add_with_many_existing_solvers": 366,
Expand Down Expand Up @@ -144,6 +146,7 @@
"transfer_authority/manager_can_transfer_solver_authority": 333,
"transfer_authority/reclaim_authority_can_transfer_itself": 333,
"transfer_authority/settlement_owned_order_authority_can_transfer_itself": 333,
"transfer_authority/solver_authority_can_transfer_itself": 333
"transfer_authority/solver_authority_can_transfer_itself": 333,
"withdraw_native_sol/happy_path_withdraws_to_a_recipient_chosen_by_the_authority": 374
}
}
2 changes: 2 additions & 0 deletions client/src/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod initialize;
pub mod reclaim_buffer;
pub mod remove_solver;
pub mod transfer_authority;
pub mod withdraw_native_sol;

pub use add_solver::AddSolver;
pub use begin_settle::{BeginSettle, InitializedIntent, Pull, TokenProgram};
Expand All @@ -28,3 +29,4 @@ pub use initialize::Initialize;
pub use reclaim_buffer::ReclaimBuffer;
pub use remove_solver::RemoveSolver;
pub use transfer_authority::TransferAuthority;
pub use withdraw_native_sol::WithdrawNativeSol;
30 changes: 30 additions & 0 deletions client/src/instruction/withdraw_native_sol.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Builder for the `WithdrawNativeSol` instruction.

use cow_settlement_interface::{
pda::{buffer::find_native_sol_buffer_pda, state::find_state_pda},
Instruction, Pubkey,
};

/// Moves `amount` lamports out of the native SOL buffer to `recipient`.
/// Authorized by `authority`, the settlement-owned-order authority, which picks
/// `recipient` freely and may set it to itself.
pub struct WithdrawNativeSol {
pub program_id: Pubkey,
pub authority: Pubkey,
pub recipient: Pubkey,
pub amount: u64,
}

impl From<WithdrawNativeSol> for Instruction {
fn from(builder: WithdrawNativeSol) -> Self {
cow_settlement_interface::instruction::withdraw_native_sol::WithdrawNativeSol {
program_id: builder.program_id,
state_pda: find_state_pda(&builder.program_id).0,
authority: builder.authority,
native_sol_buffer: find_native_sol_buffer_pda(&builder.program_id).0,
recipient: builder.recipient,
amount: builder.amount,
}
.into()
}
}
15 changes: 15 additions & 0 deletions client/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use cow_settlement_interface::{
remove_solver::RemoveSolverInput,
settle::{BeginSettleInput, FinalizeSettleInput},
transfer_authority::TransferAuthorityInput,
withdraw_native_sol::WithdrawNativeSolInput,
InstructionInputParsing,
},
recover_discriminator, SettlementInstruction,
Expand All @@ -36,6 +37,7 @@ pub enum ParsedInstruction<'a, A> {
TransferAuthority(TransferAuthorityInput<'a, A>),
AddSolver(AddSolverInput<'a, A>),
RemoveSolver(RemoveSolverInput<'a, A>),
WithdrawNativeSol(WithdrawNativeSolInput<'a, A>),
}

/// Parses any settlement instruction by its discriminator.
Expand Down Expand Up @@ -83,6 +85,9 @@ pub fn parse_instruction<'a, A>(
SettlementInstruction::RemoveSolver => ParsedInstruction::RemoveSolver(
RemoveSolverInput::parse_body(remaining_data, accounts)?,
),
SettlementInstruction::WithdrawNativeSol => ParsedInstruction::WithdrawNativeSol(
WithdrawNativeSolInput::parse_body(remaining_data, accounts)?,
),
})
}

Expand All @@ -92,6 +97,7 @@ mod tests {
use crate::instruction::{
AddSolver, BeginSettle, CancelOrder, CreateBuffers, CreateOrder,
CreateSettlementOwnedOrder, FinalizeSettle, Initialize, InitializedIntent, RemoveSolver,
WithdrawNativeSol,
};
use cow_settlement_interface::{
data::intent::fixtures::sample_intent,
Expand Down Expand Up @@ -206,6 +212,13 @@ mod tests {
solver: pubkey_from_seed("solver"),
}
.into(),
SettlementInstruction::WithdrawNativeSol => WithdrawNativeSol {
program_id,
authority: payer,
recipient: payer,
amount: 42,
}
.into(),
}
}

Expand All @@ -227,6 +240,7 @@ mod tests {
SettlementInstruction::TransferAuthority,
SettlementInstruction::AddSolver,
SettlementInstruction::RemoveSolver,
SettlementInstruction::WithdrawNativeSol,
] {
let ix = build(expected);
let accounts: Vec<_> = ix
Expand All @@ -251,6 +265,7 @@ mod tests {
ParsedInstruction::TransferAuthority(_) => SettlementInstruction::TransferAuthority,
ParsedInstruction::AddSolver(_) => SettlementInstruction::AddSolver,
ParsedInstruction::RemoveSolver(_) => SettlementInstruction::RemoveSolver,
ParsedInstruction::WithdrawNativeSol(_) => SettlementInstruction::WithdrawNativeSol,
};
assert_eq!(actual, expected);
}
Expand Down
11 changes: 9 additions & 2 deletions interface/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,19 @@ pub enum SettlementError {
/// buffer the push draws from, so settling it would move no funds yet still
/// record the declared amount as received.
PushSourceIsDestination = 44,
/// `FinalizeSettle`: the native SOL payouts would empty the native SOL
/// buffer, which the runtime would then delete.
/// Moving lamports out of the native SOL buffer would empty it, which the
/// runtime would then delete.
NativeSolBufferEmptied = 45,
Comment thread
fedgiac marked this conversation as resolved.
/// A buffer to be reclaimed holds more tokens than the burn limit supplied
/// for it, so its balance can't be cleared for closing.
ReclaimBufferBurnLimitExceeded = 46,
/// `WithdrawNativeSol`'s authority account isn't a signer, or doesn't
/// match the `settlement_owned_order_authority` recorded in the settlement
/// state PDA.
UnauthorizedNativeSolWithdrawal = 47,
/// `WithdrawNativeSol`'s native SOL buffer account isn't the canonical
/// native SOL buffer PDA.
NativeSolBufferMismatch = 48,
Comment on lines +156 to +162

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this PR, and not before the audit, but we should really clean up all our errors.
This for example should be the same as the settlement-owned order and should have a more generic name. No reason to separate the two. Same for the mismatch, I'm sure there's a generic one.

}

impl From<SettlementError> for u32 {
Expand Down
6 changes: 6 additions & 0 deletions interface/src/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod reclaim_order;
pub mod remove_solver;
pub mod settle;
pub mod transfer_authority;
pub mod withdraw_native_sol;

#[derive(Clone, Copy, Debug, Eq, PartialEq, num_enum::TryFromPrimitive)]
#[repr(u8)]
Expand Down Expand Up @@ -69,6 +70,11 @@ pub enum SettlementInstruction {
/// If the order PDA doesn't exist yet, it's created already cancelled. For
/// this, some arbitrary account needs to pay the rent and sign.
CancelOrder = 11,
/// Moves lamports out of the native SOL buffer to a recipient of the
/// caller's choosing. Only the current holder of the
/// SettlementOwnedOrderAuthority role may authorize this, and the buffer's
/// rent can't be withdrawn.
WithdrawNativeSol = 12,
}

impl SettlementInstruction {
Expand Down
Loading
Loading