Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,585 changes: 450 additions & 6,135 deletions Cargo.lock

Large diffs are not rendered by default.

65 changes: 33 additions & 32 deletions contracts/cross_chain_payload/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,67 +1,68 @@
use soroban_sdk::contracttype;
use soroban_sdk::contracterror;

/// Errors that can occur during cross-chain payload verification
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum CrossChainError {
/// Payload hash does not match
InvalidPayloadHash,
InvalidPayloadHash = 1,
/// One or more signatures are invalid
InvalidSignature,
InvalidSignature = 2,
/// Not enough signatures to reach consensus
InsufficientSignatures,
InsufficientSignatures = 3,
/// Signature verification failed with unknown error
SignatureVerificationFailed,
SignatureVerificationFailed = 4,
/// Payload has expired
PayloadExpired,
PayloadExpired = 5,
/// Payload hash already verified (replay attack detected)
ReplayAttack,
ReplayAttack = 6,
/// Validator set is invalid or missing
InvalidValidatorSet,
InvalidValidatorSet = 7,
/// Validator is not in the active set
ValidatorNotInSet,
ValidatorNotInSet = 8,
/// Sender is not authorized to execute payload
UnauthorizedSender,
UnauthorizedSender = 9,
/// Recipient chain or address is invalid
InvalidRecipient,
InvalidRecipient = 10,
/// Source chain is not recognized
UnknownSourceChain,
UnknownSourceChain = 11,
/// Destination chain is not accessible
InaccessibleDestinationChain,
InaccessibleDestinationChain = 12,
/// Bridge between chains is disabled or inactive
BridgeInactive,
BridgeInactive = 13,
/// Payload data is malformed
MalformedPayload,
MalformedPayload = 14,
/// Encoding/decoding of payload failed
EncodingError,
EncodingError = 15,
/// Operation is not supported
UnsupportedOperation,
UnsupportedOperation = 16,
/// Gas limit is too low for execution
InsufficientGas,
InsufficientGas = 17,
/// Verification context is missing required data
IncompleteVerificationContext,
IncompleteVerificationContext = 18,
/// Nonce has already been used (replay protection)
NonceAlreadyUsed,
NonceAlreadyUsed = 19,
/// Timestamp is too far in the past or future
InvalidTimestamp,
InvalidTimestamp = 20,
/// Sequence number is out of order
SequenceOutOfOrder,
SequenceOutOfOrder = 21,
/// Cross-chain contract is in maintenance mode
MaintenanceMode,
MaintenanceMode = 22,
/// Generic verification failure
VerificationFailed,
VerificationFailed = 23,
/// Too many payloads pending verification
BacklogExceeded,
BacklogExceeded = 24,
/// Bridge fee validation failed
FeeValidationFailed,
FeeValidationFailed = 25,
/// Liquidity pool error
LiquidityError,
LiquidityError = 26,
/// Storage operation failed
StorageError,
StorageError = 27,
/// Unauthorized operation
Unauthorized,
Unauthorized = 28,
/// Generic error
Unknown,
Unknown = 255,
}

impl CrossChainError {
Expand Down
2 changes: 1 addition & 1 deletion contracts/error_codes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ crate-type = ["rlib"]
soroban-sdk = "22.0.0"

[dev-dependencies]
soroban-sdk = { version = "22.0.0", features = ["testutils"] }
soroban-sdk = "22.0.0"
25 changes: 25 additions & 0 deletions contracts/error_codes/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ContractError",
"type": "object",
"description": "Explicit numerical discriminant mappings for SoroScope smart contract error codes.",
"error_codes": [
{ "name": "AlreadyInitialized", "code": 1, "description": "Contract has already been initialized." },
{ "name": "NotInitialized", "code": 2, "description": "Contract has not been initialized yet." },
{ "name": "Unauthorized", "code": 3, "description": "Caller is not authorized to perform this operation." },
{ "name": "InsufficientBalance", "code": 4, "description": "Account balance is insufficient for requested transaction." },
{ "name": "InsufficientLiquidity", "code": 5, "description": "Liquidity pool reserve is insufficient." },
{ "name": "InsufficientShares", "code": 6, "description": "LP share balance is insufficient." },
{ "name": "InsufficientAllowance", "code": 7, "description": "Approved token allowance is insufficient." },
{ "name": "SlippageExceeded", "code": 8, "description": "Slippage tolerance was exceeded during swap." },
{ "name": "InvalidFee", "code": 9, "description": "Fee parameter exceeds allowed threshold." },
{ "name": "NoPendingFeeUpdate", "code": 10, "description": "No pending fee update was found." },
{ "name": "TimelockNotElapsed", "code": 11, "description": "Timelock period has not yet elapsed." },
{ "name": "OracleNotConfigured", "code": 12, "description": "Price oracle is not configured." },
{ "name": "InvalidOraclePrice", "code": 13, "description": "Price returned by oracle is invalid or stale." },
{ "name": "Paused", "code": 14, "description": "Contract execution is currently paused by emergency guard." },
{ "name": "Overflow", "code": 15, "description": "Arithmetic overflow occurred." },
{ "name": "DivisionByZero", "code": 16, "description": "Attempted division by zero." },
{ "name": "InvalidInput", "code": 17, "description": "Input argument provided is invalid." }
]
}
102 changes: 102 additions & 0 deletions contracts/error_codes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,105 @@ pub enum ContractError {
DivisionByZero = 16,
InvalidInput = 17,
}

impl ContractError {
/// Convert the error variant into its explicit numerical u32 discriminant.
pub fn as_u32(&self) -> u32 {
*self as u32
}

/// Try to construct a `ContractError` from a numerical u32 discriminant.
pub fn from_u32(code: u32) -> Option<Self> {
match code {
1 => Some(Self::AlreadyInitialized),
2 => Some(Self::NotInitialized),
3 => Some(Self::Unauthorized),
4 => Some(Self::InsufficientBalance),
5 => Some(Self::InsufficientLiquidity),
6 => Some(Self::InsufficientShares),
7 => Some(Self::InsufficientAllowance),
8 => Some(Self::SlippageExceeded),
9 => Some(Self::InvalidFee),
10 => Some(Self::NoPendingFeeUpdate),
11 => Some(Self::TimelockNotElapsed),
12 => Some(Self::OracleNotConfigured),
13 => Some(Self::InvalidOraclePrice),
14 => Some(Self::Paused),
15 => Some(Self::Overflow),
16 => Some(Self::DivisionByZero),
17 => Some(Self::InvalidInput),
_ => None,
}
}
}

/// JSON schema specification export for cross-language error decoding.
pub const ERROR_SCHEMA_JSON: &str = r#"{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ContractError",
"type": "object",
"description": "Explicit numerical discriminant mappings for SoroScope smart contract error codes.",
"error_codes": [
{ "name": "AlreadyInitialized", "code": 1, "description": "Contract has already been initialized." },
{ "name": "NotInitialized", "code": 2, "description": "Contract has not been initialized yet." },
{ "name": "Unauthorized", "code": 3, "description": "Caller is not authorized to perform this operation." },
{ "name": "InsufficientBalance", "code": 4, "description": "Account balance is insufficient for requested transaction." },
{ "name": "InsufficientLiquidity", "code": 5, "description": "Liquidity pool reserve is insufficient." },
{ "name": "InsufficientShares", "code": 6, "description": "LP share balance is insufficient." },
{ "name": "InsufficientAllowance", "code": 7, "description": "Approved token allowance is insufficient." },
{ "name": "SlippageExceeded", "code": 8, "description": "Slippage tolerance was exceeded during swap." },
{ "name": "InvalidFee", "code": 9, "description": "Fee parameter exceeds allowed threshold." },
{ "name": "NoPendingFeeUpdate", "code": 10, "description": "No pending fee update was found." },
{ "name": "TimelockNotElapsed", "code": 11, "description": "Timelock period has not yet elapsed." },
{ "name": "OracleNotConfigured", "code": 12, "description": "Price oracle is not configured." },
{ "name": "InvalidOraclePrice", "code": 13, "description": "Price returned by oracle is invalid or stale." },
{ "name": "Paused", "code": 14, "description": "Contract execution is currently paused by emergency guard." },
{ "name": "Overflow", "code": 15, "description": "Arithmetic overflow occurred." },
{ "name": "DivisionByZero", "code": 16, "description": "Attempted division by zero." },
{ "name": "InvalidInput", "code": 17, "description": "Input argument provided is invalid." }
]
}"#;

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_explicit_numerical_discriminants() {
assert_eq!(ContractError::AlreadyInitialized.as_u32(), 1);
assert_eq!(ContractError::NotInitialized.as_u32(), 2);
assert_eq!(ContractError::Unauthorized.as_u32(), 3);
assert_eq!(ContractError::InsufficientBalance.as_u32(), 4);
assert_eq!(ContractError::InsufficientLiquidity.as_u32(), 5);
assert_eq!(ContractError::InsufficientShares.as_u32(), 6);
assert_eq!(ContractError::InsufficientAllowance.as_u32(), 7);
assert_eq!(ContractError::SlippageExceeded.as_u32(), 8);
assert_eq!(ContractError::InvalidFee.as_u32(), 9);
assert_eq!(ContractError::NoPendingFeeUpdate.as_u32(), 10);
assert_eq!(ContractError::TimelockNotElapsed.as_u32(), 11);
assert_eq!(ContractError::OracleNotConfigured.as_u32(), 12);
assert_eq!(ContractError::InvalidOraclePrice.as_u32(), 13);
assert_eq!(ContractError::Paused.as_u32(), 14);
assert_eq!(ContractError::Overflow.as_u32(), 15);
assert_eq!(ContractError::DivisionByZero.as_u32(), 16);
assert_eq!(ContractError::InvalidInput.as_u32(), 17);
}

#[test]
fn test_from_u32_conversion() {
for code in 1..=17 {
let err = ContractError::from_u32(code).expect("Valid discriminant should convert");
assert_eq!(err.as_u32(), code);
}
assert_eq!(ContractError::from_u32(0), None);
assert_eq!(ContractError::from_u32(18), None);
}

#[test]
fn test_schema_json_contains_all_variants() {
assert!(ERROR_SCHEMA_JSON.contains("AlreadyInitialized"));
assert!(ERROR_SCHEMA_JSON.contains("InvalidInput"));
assert!(ERROR_SCHEMA_JSON.contains("\"code\": 1"));
assert!(ERROR_SCHEMA_JSON.contains("\"code\": 17"));
}
}
20 changes: 0 additions & 20 deletions contracts/liquidity_pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,6 @@ pub enum Error {
InvalidFee = 9,
OracleNotConfigured = 10,
InvalidOraclePrice = 11,
NotInitialized = 2,
Unauthorized = 3,
InsufficientBalance = 4,
InsufficientLiquidity = 5,
InsufficientShares = 6,
InsufficientAllowance = 7,
SlippageExceeded = 8,
InvalidFee = 9,
OracleNotConfigured = 10,
PendingFeeUpdateExists = 11,
InsufficientLiquidity = 2,
SlippageExceeded = 3,
InsufficientShares = 4,
NotInitialized = 5,
InsufficientBalance = 6,
Unauthorized = 7,
InsufficientAllowance = 8,
InvalidFee = 9,
OracleNotConfigured = 10,
InvalidOraclePrice = 11,
TimelockNotElapsed = 12,
NoPendingFeeUpdate = 13,
Paused = 14,
Expand Down
34 changes: 30 additions & 4 deletions contracts/token/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::allowance::{read_allowance, spend_allowance, write_allowance};
use crate::balance::{read_balance, receive_balance, spend_balance};
use crate::metadata::{read_decimal, read_name, read_symbol, write_metadata};
use emergency_guard::{EmergencyGuard, GuardError, PauseType};
use soroban_sdk::{contract, contractimpl, vec, Address, Env, String, Vec};
use soroban_sdk::{contract, contractimpl, contracttype, vec, Address, Env, String, Vec};

fn require_not_paused(e: &Env, operation: u32) {
if EmergencyGuard::is_paused(e.clone(), operation) {
Expand Down Expand Up @@ -43,6 +43,14 @@ pub trait TokenTrait {
fn symbol(e: Env) -> String;
}

#[derive(Clone, Debug, Eq, PartialEq)]
#[contracttype]
pub struct BurnEvent {
pub burner: Address,
pub target_account: Address,
pub amount: i128,
}

#[contract]
pub struct Token;

Expand Down Expand Up @@ -161,16 +169,34 @@ impl TokenTrait for Token {
from.require_auth();
e.storage().instance().extend_ttl(100, 100);

spend_balance(&e, from, amount);
spend_balance(&e, from.clone(), amount);

e.events().publish(
(String::from_str(&e, "burn"), from.clone()),
BurnEvent {
burner: from.clone(),
target_account: from,
amount,
},
);
}

fn burn_from(e: Env, spender: Address, from: Address, amount: i128) {
require_not_paused(&e, PauseType::BURN);
spender.require_auth();
e.storage().instance().extend_ttl(100, 100);

spend_allowance(&e, from.clone(), spender, amount);
spend_balance(&e, from, amount);
spend_allowance(&e, from.clone(), spender.clone(), amount);
spend_balance(&e, from.clone(), amount);

e.events().publish(
(String::from_str(&e, "burn"), from.clone()),
BurnEvent {
burner: spender,
target_account: from,
amount,
},
);
}

fn decimals(e: Env) -> u32 {
Expand Down
Loading