mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-14 15:09:23 +00:00
Compare commits
2 Commits
2fbe925c4d
...
next-polka
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee9b9778b5 | ||
|
|
5a3cf1f2be |
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -4148,8 +4148,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.99.0"
|
||||||
source = "git+https://github.com/rust-lang-nursery/lazy-static.rs?rev=5735630d46572f1e5377c8f2ba0f79d18f53b10c#5735630d46572f1e5377c8f2ba0f79d18f53b10c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leb128fmt"
|
name = "leb128fmt"
|
||||||
@@ -8433,6 +8432,7 @@ name = "serai-in-instructions-pallet"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec",
|
"bitvec",
|
||||||
|
"borsh",
|
||||||
"frame-support",
|
"frame-support",
|
||||||
"frame-system",
|
"frame-system",
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
|
|||||||
@@ -200,8 +200,9 @@ dalek-ff-group = { path = "patches/dalek-ff-group" }
|
|||||||
minimal-ed448 = { path = "crypto/ed448" }
|
minimal-ed448 = { path = "crypto/ed448" }
|
||||||
modular-frost = { path = "crypto/frost" }
|
modular-frost = { path = "crypto/frost" }
|
||||||
|
|
||||||
# Patch due to `std` now including the required functionality
|
# Patches due to `std` now including the required functionality
|
||||||
is_terminal_polyfill = { path = "./patches/is_terminal_polyfill" }
|
is_terminal_polyfill = { path = "patches/is_terminal_polyfill" }
|
||||||
|
lazy_static = { path = "patches/lazy_static" }
|
||||||
# This has a non-deprecated `std` alternative since Rust's 2024 edition
|
# This has a non-deprecated `std` alternative since Rust's 2024 edition
|
||||||
home = { path = "patches/home" }
|
home = { path = "patches/home" }
|
||||||
|
|
||||||
@@ -209,9 +210,6 @@ home = { path = "patches/home" }
|
|||||||
darling = { path = "patches/darling" }
|
darling = { path = "patches/darling" }
|
||||||
thiserror = { path = "patches/thiserror" }
|
thiserror = { path = "patches/thiserror" }
|
||||||
|
|
||||||
# https://github.com/rust-lang-nursery/lazy-static.rs/issues/201
|
|
||||||
lazy_static = { git = "https://github.com/rust-lang-nursery/lazy-static.rs", rev = "5735630d46572f1e5377c8f2ba0f79d18f53b10c" }
|
|
||||||
|
|
||||||
# directories-next was created because directories was unmaintained
|
# directories-next was created because directories was unmaintained
|
||||||
# directories-next is now unmaintained while directories is maintained
|
# directories-next is now unmaintained while directories is maintained
|
||||||
# The directories author pulls in ridiculously pointless crates and prefers
|
# The directories author pulls in ridiculously pointless crates and prefers
|
||||||
|
|||||||
@@ -147,7 +147,6 @@ unknown-registry = "deny"
|
|||||||
unknown-git = "deny"
|
unknown-git = "deny"
|
||||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||||
allow-git = [
|
allow-git = [
|
||||||
"https://github.com/rust-lang-nursery/lazy-static.rs",
|
|
||||||
"https://github.com/kayabaNerve/elliptic-curves",
|
"https://github.com/kayabaNerve/elliptic-curves",
|
||||||
"https://github.com/monero-oxide/monero-oxide",
|
"https://github.com/monero-oxide/monero-oxide",
|
||||||
"https://github.com/serai-dex/patch-polkadot-sdk",
|
"https://github.com/serai-dex/patch-polkadot-sdk",
|
||||||
|
|||||||
16
patches/lazy_static/Cargo.toml
Normal file
16
patches/lazy_static/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.99.0"
|
||||||
|
description = "`lazy_static` which patches to `std::sync::LazyLock`"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/patches/lazy_static"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
keywords = []
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.80"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
14
patches/lazy_static/src/lib.rs
Normal file
14
patches/lazy_static/src/lib.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! lazy_static {
|
||||||
|
($($(#[$attr: meta])* $vis: vis static ref $name: ident: $type: ty = $value: expr;)*) => {
|
||||||
|
$(
|
||||||
|
$(#[$attr])*
|
||||||
|
$vis static $name: std::sync::LazyLock<$type> = std::sync::LazyLock::new(|| $value);
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Explicitly initialize a static declared with [`lazy_static`].
|
||||||
|
pub fn initialize<T, F: Fn() -> T>(lazy: &std::sync::LazyLock<T, F>) {
|
||||||
|
std::sync::LazyLock::force(lazy);
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use serai_primitives::{
|
use serai_primitives::{
|
||||||
BlockHash, network_id::ExternalNetworkId, validator_sets::Session, instructions::SignedBatch,
|
BlockHash, network_id::ExternalNetworkId, validator_sets::Session, address::SeraiAddress,
|
||||||
|
instructions::SignedBatch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The address used for executing `InInstruction`s.
|
||||||
|
pub fn address() -> SeraiAddress {
|
||||||
|
SeraiAddress::system(borsh::to_vec(b"InInstructions").unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
/// A call to `InInstruction`s.
|
/// A call to `InInstruction`s.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum Call {
|
pub enum Call {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
|
borsh = { version = "1", default-features = false }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
|
sp-application-crypto = { git = "https://github.com/serai-dex/patch-polkadot-sdk", default-features = false }
|
||||||
@@ -40,6 +41,7 @@ serai-genesis-liquidity-pallet = { path = "../genesis-liquidity", default-featur
|
|||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
"scale/std",
|
"scale/std",
|
||||||
|
"borsh/std",
|
||||||
|
|
||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-application-crypto/std",
|
"sp-application-crypto/std",
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ extern crate alloc;
|
|||||||
#[expect(clippy::cast_possible_truncation)]
|
#[expect(clippy::cast_possible_truncation)]
|
||||||
#[frame_support::pallet]
|
#[frame_support::pallet]
|
||||||
mod pallet {
|
mod pallet {
|
||||||
|
use sp_core::sr25519::Public;
|
||||||
use sp_application_crypto::RuntimePublic;
|
use sp_application_crypto::RuntimePublic;
|
||||||
|
|
||||||
|
use frame_support::{pallet_prelude::*, dispatch::RawOrigin};
|
||||||
use frame_system::pallet_prelude::*;
|
use frame_system::pallet_prelude::*;
|
||||||
use frame_support::pallet_prelude::*;
|
|
||||||
|
|
||||||
use serai_abi::{primitives::prelude::*, in_instructions::Event};
|
use serai_abi::{primitives::prelude::*, in_instructions::Event};
|
||||||
|
|
||||||
@@ -59,6 +60,127 @@ mod pallet {
|
|||||||
fn emit_event(event: Event) {
|
fn emit_event(event: Event) {
|
||||||
Core::<T>::emit_event(event)
|
Core::<T>::emit_event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute an `InInstructionWithBalance`.
|
||||||
|
///
|
||||||
|
/// We execute this within a database layer to ensure it's atomic, executing entirely or not at
|
||||||
|
/// all.
|
||||||
|
#[frame_support::transactional]
|
||||||
|
fn execute(instruction: InInstructionWithBalance) -> DispatchResult {
|
||||||
|
let InInstructionWithBalance { instruction, balance: external_balance } = instruction;
|
||||||
|
let balance = <Balance as From<ExternalBalance>>::from(external_balance);
|
||||||
|
|
||||||
|
// Mint the balance to ourself
|
||||||
|
let address = serai_abi::in_instructions::address();
|
||||||
|
Coins::<T>::mint(address.into(), balance)?;
|
||||||
|
|
||||||
|
match instruction {
|
||||||
|
InInstruction::GenesisLiquidity(address) => {
|
||||||
|
serai_genesis_liquidity_pallet::Pallet::<T>::add_liquidity(address, external_balance)?;
|
||||||
|
}
|
||||||
|
InInstruction::SwapToStakedSri { validator, minimum } => todo!("TODO"),
|
||||||
|
InInstruction::TransferWithSwap { to, maximum_to_swap, sri } => {
|
||||||
|
serai_dex_pallet::Pallet::<T>::swap_for(
|
||||||
|
RawOrigin::Signed(address.into()).into(),
|
||||||
|
Balance { coin: Coin::Serai, amount: sri },
|
||||||
|
Balance { coin: balance.coin, amount: maximum_to_swap },
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Coins::<T>::transfer_fn(
|
||||||
|
address.into(),
|
||||||
|
to.into(),
|
||||||
|
Balance {
|
||||||
|
coin: balance.coin,
|
||||||
|
amount: Coins::<T>::balance(Public::from(address), balance.coin),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Coins::<T>::transfer_fn(
|
||||||
|
address.into(),
|
||||||
|
to.into(),
|
||||||
|
Balance {
|
||||||
|
coin: Coin::Serai,
|
||||||
|
amount: Coins::<T>::balance(Public::from(address), Coin::Serai),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
InInstruction::Transfer { to } => {
|
||||||
|
Coins::<T>::transfer_fn(address.into(), to.into(), balance)?;
|
||||||
|
}
|
||||||
|
InInstruction::SwapAndAddLiquidity {
|
||||||
|
address: destination,
|
||||||
|
coin,
|
||||||
|
sri_minimum,
|
||||||
|
sri_for_fees,
|
||||||
|
} => {
|
||||||
|
let external_coin = external_balance.coin;
|
||||||
|
serai_dex_pallet::Pallet::<T>::swap(
|
||||||
|
RawOrigin::Signed(address.into()).into(),
|
||||||
|
Balance {
|
||||||
|
coin: Coin::External(external_coin),
|
||||||
|
amount: (balance.amount - coin).ok_or(serai_dex_pallet::Error::<T>::Underflow)?,
|
||||||
|
},
|
||||||
|
Balance {
|
||||||
|
coin: Coin::Serai,
|
||||||
|
amount: (sri_minimum + sri_for_fees).ok_or(serai_dex_pallet::Error::<T>::Overflow)?,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let sri_intended = (Coins::<T>::balance(Public::from(address), Coin::Serai) -
|
||||||
|
sri_for_fees)
|
||||||
|
.expect("swapped to amount sufficient for minimum, fees, but received less than fees?");
|
||||||
|
let coin_intended = coin;
|
||||||
|
let coin_minimum = coin;
|
||||||
|
serai_dex_pallet::Pallet::<T>::add_liquidity(
|
||||||
|
RawOrigin::Signed(address.into()).into(),
|
||||||
|
external_coin,
|
||||||
|
sri_intended,
|
||||||
|
coin_intended,
|
||||||
|
sri_minimum,
|
||||||
|
coin_minimum,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Transfer the rest, which will be more than the amount requested for fees, to the
|
||||||
|
// destination
|
||||||
|
Coins::<T>::transfer_fn(
|
||||||
|
address.into(),
|
||||||
|
destination.into(),
|
||||||
|
Balance {
|
||||||
|
coin: Coin::Serai,
|
||||||
|
amount: Coins::<T>::balance(Public::from(address), Coin::Serai),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
InInstruction::Swap { address: destination, minimum_to_receive } => {
|
||||||
|
serai_dex_pallet::Pallet::<T>::swap(
|
||||||
|
RawOrigin::Signed(address.into()).into(),
|
||||||
|
balance,
|
||||||
|
minimum_to_receive,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let coin = minimum_to_receive.coin;
|
||||||
|
let received_amount = Coins::<T>::balance(Public::from(address), coin);
|
||||||
|
let received = Balance { coin, amount: received_amount };
|
||||||
|
Coins::<T>::transfer_fn(address.into(), destination.into(), received)?;
|
||||||
|
}
|
||||||
|
InInstruction::SwapOut { instruction, minimum_to_receive } => {
|
||||||
|
serai_dex_pallet::Pallet::<T>::swap(
|
||||||
|
RawOrigin::Signed(address.into()).into(),
|
||||||
|
balance,
|
||||||
|
minimum_to_receive.into(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let coin = minimum_to_receive.coin;
|
||||||
|
let received_amount = Coins::<T>::balance(Public::from(address), Coin::from(coin));
|
||||||
|
let received = ExternalBalance { coin, amount: received_amount };
|
||||||
|
Coins::<T>::burn_with_instruction(
|
||||||
|
RawOrigin::Signed(address.into()).into(),
|
||||||
|
OutInstructionWithBalance { instruction, balance: received },
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::call]
|
#[pallet::call]
|
||||||
@@ -67,7 +189,29 @@ mod pallet {
|
|||||||
#[pallet::call_index(0)]
|
#[pallet::call_index(0)]
|
||||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||||
pub fn execute_batch(origin: OriginFor<T>, batch: SignedBatch) -> DispatchResult {
|
pub fn execute_batch(origin: OriginFor<T>, batch: SignedBatch) -> DispatchResult {
|
||||||
todo!("TODO")
|
let batch = batch.batch;
|
||||||
|
let network = batch.network();
|
||||||
|
|
||||||
|
let mut in_instruction_results = bitvec::vec::BitVec::new();
|
||||||
|
for instruction in batch.instructions() {
|
||||||
|
in_instruction_results.push(Self::execute(instruction.clone()).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The publishing session is always the current session
|
||||||
|
let publishing_session =
|
||||||
|
serai_validator_sets_pallet::Pallet::<T>::current_session(NetworkId::from(network))
|
||||||
|
.expect("`SignedBatch` for a network without a session was validated");
|
||||||
|
|
||||||
|
Self::emit_event(Event::Batch {
|
||||||
|
network,
|
||||||
|
publishing_session,
|
||||||
|
id: batch.id(),
|
||||||
|
external_network_block_hash: batch.external_network_block_hash(),
|
||||||
|
in_instructions_hash: sp_core::blake2_256(&borsh::to_vec(batch.instructions()).unwrap()),
|
||||||
|
in_instruction_results,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +265,13 @@ mod pallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify every coin with the `Batch` corresponds to this network
|
||||||
|
for instruction in batch.batch.instructions() {
|
||||||
|
if instruction.balance.coin.network() != network {
|
||||||
|
Err(InvalidTransaction::Custom(2))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Verify this is the first `Batch` for this block
|
// Verify this is the first `Batch` for this block
|
||||||
let current_block_number = frame_system::Pallet::<T>::block_number();
|
let current_block_number = frame_system::Pallet::<T>::block_number();
|
||||||
if BlockOfLastBatch::<T>::get(network) == Some(current_block_number) {
|
if BlockOfLastBatch::<T>::get(network) == Some(current_block_number) {
|
||||||
|
|||||||
@@ -10,15 +10,6 @@ use crate::{
|
|||||||
mod batch;
|
mod batch;
|
||||||
pub use batch::*;
|
pub use batch::*;
|
||||||
|
|
||||||
/// The destination for coins.
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
|
||||||
pub enum Destination {
|
|
||||||
/// The Serai address to transfer the coins to.
|
|
||||||
Serai(SeraiAddress),
|
|
||||||
/// Burn the coins with the included `OutInstruction`.
|
|
||||||
Burn(OutInstruction),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An instruction on how to handle coins in.
|
/// An instruction on how to handle coins in.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum InInstruction {
|
pub enum InInstruction {
|
||||||
@@ -33,10 +24,10 @@ pub enum InInstruction {
|
|||||||
},
|
},
|
||||||
/// Transfer the coins to a Serai address, swapping some for SRI.
|
/// Transfer the coins to a Serai address, swapping some for SRI.
|
||||||
TransferWithSwap {
|
TransferWithSwap {
|
||||||
/// The Serai address to transfer the coins to, after swapping some.
|
/// The Serai address to transfer the coins to.
|
||||||
to: SeraiAddress,
|
to: SeraiAddress,
|
||||||
/// The maximum amount of coins to swap for the intended amount of SRI.
|
/// The maximum amount of coins to swap for the intended amount of SRI.
|
||||||
maximum_swap: Amount,
|
maximum_to_swap: Amount,
|
||||||
/// The SRI amount to swap some of the coins for.
|
/// The SRI amount to swap some of the coins for.
|
||||||
sri: Amount,
|
sri: Amount,
|
||||||
},
|
},
|
||||||
@@ -47,23 +38,28 @@ pub enum InInstruction {
|
|||||||
},
|
},
|
||||||
/// Swap part of the coins to SRI and add the coins as liquidity.
|
/// Swap part of the coins to SRI and add the coins as liquidity.
|
||||||
SwapAndAddLiquidity {
|
SwapAndAddLiquidity {
|
||||||
/// The owner to-be of the added liquidity.
|
/// The recipient to-be of the added liquidity.
|
||||||
owner: SeraiAddress,
|
address: SeraiAddress,
|
||||||
/// The amount of SRI to add within the liquidity position.
|
/// The amount of the coin to add within the liquidity position.
|
||||||
sri: Amount,
|
coin: Amount,
|
||||||
/// The minimum amount of the coin to add as liquidity.
|
/// The minimum amount of SRI to add as liquidity.
|
||||||
minimum_coin: Amount,
|
sri_minimum: Amount,
|
||||||
/// The amount of SRI to swap to and send to the owner to-be to pay for transactions on Serai.
|
/// The amount of SRI to swap to and send to the owner to-be to pay for transactions on Serai.
|
||||||
sri_for_fees: Amount,
|
sri_for_fees: Amount,
|
||||||
},
|
},
|
||||||
/// Swap the coins.
|
/// Swap the coins.
|
||||||
Swap {
|
Swap {
|
||||||
/// The minimum balance to receive.
|
/// The destination to received the coins swapped to with.
|
||||||
minimum_out: Balance,
|
address: SeraiAddress,
|
||||||
/// The destination to transfer the balance to.
|
/// The minimum balance to receive from the swap.
|
||||||
///
|
minimum_to_receive: Balance,
|
||||||
/// If `Destination::Burn`, the balance out will be burnt with the included `OutInstruction`.
|
},
|
||||||
destination: Destination,
|
/// Swap the coins, burning them with the included `OutInstruction`.
|
||||||
|
SwapOut {
|
||||||
|
/// The instruction to burn the coins swapped to with.
|
||||||
|
instruction: OutInstruction,
|
||||||
|
/// The minimum balance to receive from the swap.
|
||||||
|
minimum_to_receive: ExternalBalance,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user