mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-12 14:09:25 +00:00
Compare commits
18 Commits
next
...
dab1bab736
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dab1bab736 | ||
|
|
d46ca1e8d0 | ||
|
|
1a766ab773 | ||
|
|
df2ae10d2f | ||
|
|
b92ac4a15b | ||
|
|
51bae4fedc | ||
|
|
ee8b353132 | ||
|
|
a2d558ee34 | ||
|
|
3273a4b725 | ||
|
|
df87abbae0 | ||
|
|
fdf2ec8e92 | ||
|
|
f92fe922a6 | ||
|
|
121a48b55c | ||
|
|
dff9a04a8c | ||
|
|
2d8f70036a | ||
|
|
dd95494d9c | ||
|
|
653b0e0bbc | ||
|
|
d78c92bc3e |
2
.github/nightly-version
vendored
2
.github/nightly-version
vendored
@@ -1 +1 @@
|
|||||||
nightly-2025-01-01
|
nightly-2025-02-01
|
||||||
|
|||||||
2
.github/workflows/daily-deny.yml
vendored
2
.github/workflows/daily-deny.yml
vendored
@@ -21,4 +21,4 @@ jobs:
|
|||||||
run: cargo install --locked cargo-deny
|
run: cargo install --locked cargo-deny
|
||||||
|
|
||||||
- name: Run cargo deny
|
- name: Run cargo deny
|
||||||
run: cargo deny -L error --all-features check
|
run: cargo deny -L error --all-features check --hide-inclusion-graph
|
||||||
|
|||||||
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
uses: ./.github/actions/build-dependencies
|
uses: ./.github/actions/build-dependencies
|
||||||
|
|
||||||
- name: Install nightly rust
|
- name: Install nightly rust
|
||||||
run: rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32-unknown-unknown -c clippy
|
run: rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasmv1-none -c clippy
|
||||||
|
|
||||||
- name: Run Clippy
|
- name: Run Clippy
|
||||||
run: cargo +${{ steps.nightly.outputs.version }} clippy --all-features --all-targets -- -D warnings -A clippy::items_after_test_module
|
run: cargo +${{ steps.nightly.outputs.version }} clippy --all-features --all-targets -- -D warnings -A clippy::items_after_test_module
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
run: cargo install --locked cargo-deny
|
run: cargo install --locked cargo-deny
|
||||||
|
|
||||||
- name: Run cargo deny
|
- name: Run cargo deny
|
||||||
run: cargo deny -L error --all-features check
|
run: cargo deny -L error --all-features check --hide-inclusion-graph
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
2
.github/workflows/pages.yml
vendored
2
.github/workflows/pages.yml
vendored
@@ -69,7 +69,7 @@ jobs:
|
|||||||
uses: ./.github/actions/build-dependencies
|
uses: ./.github/actions/build-dependencies
|
||||||
- name: Buld Rust docs
|
- name: Buld Rust docs
|
||||||
run: |
|
run: |
|
||||||
rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32-unknown-unknown -c rust-docs
|
rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasmv1-none -c rust-docs
|
||||||
RUSTDOCFLAGS="--cfg docsrs" cargo +${{ steps.nightly.outputs.version }} doc --workspace --all-features
|
RUSTDOCFLAGS="--cfg docsrs" cargo +${{ steps.nightly.outputs.version }} doc --workspace --all-features
|
||||||
mv target/doc docs/_site/rust
|
mv target/doc docs/_site/rust
|
||||||
|
|
||||||
|
|||||||
3833
Cargo.lock
generated
3833
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
34
Cargo.toml
34
Cargo.toml
@@ -103,31 +103,17 @@ members = [
|
|||||||
"coordinator",
|
"coordinator",
|
||||||
|
|
||||||
"substrate/primitives",
|
"substrate/primitives",
|
||||||
|
|
||||||
"substrate/coins/primitives",
|
|
||||||
"substrate/coins/pallet",
|
|
||||||
|
|
||||||
"substrate/dex/pallet",
|
|
||||||
|
|
||||||
"substrate/validator-sets/primitives",
|
|
||||||
"substrate/validator-sets/pallet",
|
|
||||||
|
|
||||||
"substrate/genesis-liquidity/primitives",
|
|
||||||
"substrate/genesis-liquidity/pallet",
|
|
||||||
|
|
||||||
"substrate/emissions/primitives",
|
|
||||||
"substrate/emissions/pallet",
|
|
||||||
|
|
||||||
"substrate/economic-security/pallet",
|
|
||||||
|
|
||||||
"substrate/in-instructions/primitives",
|
|
||||||
"substrate/in-instructions/pallet",
|
|
||||||
|
|
||||||
"substrate/signals/primitives",
|
|
||||||
"substrate/signals/pallet",
|
|
||||||
|
|
||||||
"substrate/abi",
|
"substrate/abi",
|
||||||
|
|
||||||
|
"substrate/coins",
|
||||||
|
"substrate/validator-sets",
|
||||||
|
"substrate/signals",
|
||||||
|
"substrate/dex",
|
||||||
|
"substrate/genesis-liquidity",
|
||||||
|
"substrate/economic-security",
|
||||||
|
"substrate/emissions",
|
||||||
|
"substrate/in-instructions",
|
||||||
|
|
||||||
"substrate/runtime",
|
"substrate/runtime",
|
||||||
"substrate/node",
|
"substrate/node",
|
||||||
|
|
||||||
@@ -208,6 +194,7 @@ directories-next = { path = "patches/directories-next" }
|
|||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
unwrap_or_default = "allow"
|
unwrap_or_default = "allow"
|
||||||
map_unwrap_or = "allow"
|
map_unwrap_or = "allow"
|
||||||
|
needless_continue = "allow"
|
||||||
borrow_as_ptr = "deny"
|
borrow_as_ptr = "deny"
|
||||||
cast_lossless = "deny"
|
cast_lossless = "deny"
|
||||||
cast_possible_truncation = "deny"
|
cast_possible_truncation = "deny"
|
||||||
@@ -238,7 +225,6 @@ manual_string_new = "deny"
|
|||||||
match_bool = "deny"
|
match_bool = "deny"
|
||||||
match_same_arms = "deny"
|
match_same_arms = "deny"
|
||||||
missing_fields_in_debug = "deny"
|
missing_fields_in_debug = "deny"
|
||||||
needless_continue = "deny"
|
|
||||||
needless_pass_by_value = "deny"
|
needless_pass_by_value = "deny"
|
||||||
ptr_cast_constness = "deny"
|
ptr_cast_constness = "deny"
|
||||||
range_minus_one = "deny"
|
range_minus_one = "deny"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub fn serai_db_key(
|
|||||||
///
|
///
|
||||||
/// Creates a unit struct and a default implementation for the `key`, `get`, and `set`. The macro
|
/// Creates a unit struct and a default implementation for the `key`, `get`, and `set`. The macro
|
||||||
/// uses a syntax similar to defining a function. Parameters are concatenated to produce a key,
|
/// uses a syntax similar to defining a function. Parameters are concatenated to produce a key,
|
||||||
/// they must be `scale` encodable. The return type is used to auto encode and decode the database
|
/// they must be `borsh` serializable. The return type is used to auto (de)serialize the database
|
||||||
/// value bytes using `borsh`.
|
/// value bytes using `borsh`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -54,11 +54,10 @@ macro_rules! create_db {
|
|||||||
)?;
|
)?;
|
||||||
impl$(<$($generic_name: $generic_type),+>)? $field_name$(<$($generic_name),+>)? {
|
impl$(<$($generic_name: $generic_type),+>)? $field_name$(<$($generic_name),+>)? {
|
||||||
pub(crate) fn key($($arg: $arg_type),*) -> Vec<u8> {
|
pub(crate) fn key($($arg: $arg_type),*) -> Vec<u8> {
|
||||||
use scale::Encode;
|
|
||||||
$crate::serai_db_key(
|
$crate::serai_db_key(
|
||||||
stringify!($db_name).as_bytes(),
|
stringify!($db_name).as_bytes(),
|
||||||
stringify!($field_name).as_bytes(),
|
stringify!($field_name).as_bytes(),
|
||||||
($($arg),*).encode()
|
&borsh::to_vec(&($($arg),*)).unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn set(
|
pub(crate) fn set(
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ messages = { package = "serai-processor-messages", path = "../processor/messages
|
|||||||
message-queue = { package = "serai-message-queue", path = "../message-queue" }
|
message-queue = { package = "serai-message-queue", path = "../message-queue" }
|
||||||
tributary-sdk = { path = "./tributary-sdk" }
|
tributary-sdk = { path = "./tributary-sdk" }
|
||||||
|
|
||||||
serai-client = { path = "../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
serai-client = { path = "../substrate/client", default-features = false, features = ["serai"] }
|
||||||
|
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }
|
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
|||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai"] }
|
||||||
|
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
|
|||||||
|
|
||||||
serai-db = { path = "../../common/db", version = "0.1" }
|
serai-db = { path = "../../common/db", version = "0.1" }
|
||||||
|
|
||||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai"] }
|
||||||
serai-cosign = { path = "../cosign" }
|
serai-cosign = { path = "../cosign" }
|
||||||
tributary-sdk = { path = "../tributary-sdk" }
|
tributary-sdk = { path = "../tributary-sdk" }
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
|
|||||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
|
|
||||||
serai-client = { path = "../../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
serai-client = { path = "../../../substrate/client", default-features = false, features = ["serai"] }
|
||||||
serai-cosign = { path = "../../cosign" }
|
serai-cosign = { path = "../../cosign" }
|
||||||
tributary-sdk = { path = "../../tributary-sdk" }
|
tributary-sdk = { path = "../../tributary-sdk" }
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
|
|||||||
|
|
||||||
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
serai-client = { path = "../../substrate/client", version = "0.1", default-features = false, features = ["serai", "borsh"] }
|
serai-client = { path = "../../substrate/client", version = "0.1", default-features = false, features = ["serai"] }
|
||||||
|
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub(crate) fn merkle(hash_args: &[[u8; 32]]) -> [u8; 32] {
|
|||||||
let zero = [0; 32];
|
let zero = [0; 32];
|
||||||
let mut interim;
|
let mut interim;
|
||||||
while hashes.len() > 1 {
|
while hashes.len() > 1 {
|
||||||
interim = Vec::with_capacity((hashes.len() + 1) / 2);
|
interim = Vec::with_capacity(hashes.len().div_ceil(2));
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < hashes.len() {
|
while i < hashes.len() {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, fea
|
|||||||
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
|
||||||
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr", default-features = false, features = ["std"] }
|
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai"] }
|
||||||
|
|
||||||
serai-db = { path = "../../common/db" }
|
serai-db = { path = "../../common/db" }
|
||||||
serai-task = { path = "../../common/task", version = "0.1" }
|
serai-task = { path = "../../common/task", version = "0.1" }
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ impl Topic {
|
|||||||
attempt: attempt + 1,
|
attempt: attempt + 1,
|
||||||
round: SigningProtocolRound::Preprocess,
|
round: SigningProtocolRound::Preprocess,
|
||||||
}),
|
}),
|
||||||
Topic::SlashReport { .. } => None,
|
Topic::SlashReport => None,
|
||||||
Topic::Sign { id, attempt, round: _ } => {
|
Topic::Sign { id, attempt, round: _ } => {
|
||||||
Some(Topic::Sign { id, attempt: attempt + 1, round: SigningProtocolRound::Preprocess })
|
Some(Topic::Sign { id, attempt: attempt + 1, round: SigningProtocolRound::Preprocess })
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ impl Topic {
|
|||||||
}
|
}
|
||||||
SigningProtocolRound::Share => None,
|
SigningProtocolRound::Share => None,
|
||||||
},
|
},
|
||||||
Topic::SlashReport { .. } => None,
|
Topic::SlashReport => None,
|
||||||
Topic::Sign { id, attempt, round } => match round {
|
Topic::Sign { id, attempt, round } => match round {
|
||||||
SigningProtocolRound::Preprocess => {
|
SigningProtocolRound::Preprocess => {
|
||||||
let attempt = attempt + 1;
|
let attempt = attempt + 1;
|
||||||
@@ -102,7 +102,7 @@ impl Topic {
|
|||||||
match self {
|
match self {
|
||||||
Topic::RemoveParticipant { .. } => None,
|
Topic::RemoveParticipant { .. } => None,
|
||||||
Topic::DkgConfirmation { .. } => None,
|
Topic::DkgConfirmation { .. } => None,
|
||||||
Topic::SlashReport { .. } => None,
|
Topic::SlashReport => None,
|
||||||
Topic::Sign { id, attempt, round: _ } => Some(SignId { session: set.session, id, attempt }),
|
Topic::Sign { id, attempt, round: _ } => Some(SignId { session: set.session, id, attempt }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ impl Topic {
|
|||||||
};
|
};
|
||||||
SignId { session: set.session, id, attempt }
|
SignId { session: set.session, id, attempt }
|
||||||
}),
|
}),
|
||||||
Topic::SlashReport { .. } => None,
|
Topic::SlashReport => None,
|
||||||
Topic::Sign { .. } => None,
|
Topic::Sign { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ impl Topic {
|
|||||||
Some(Topic::DkgConfirmation { attempt, round: SigningProtocolRound::Preprocess })
|
Some(Topic::DkgConfirmation { attempt, round: SigningProtocolRound::Preprocess })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Topic::SlashReport { .. } => None,
|
Topic::SlashReport => None,
|
||||||
Topic::Sign { id, attempt, round } => match round {
|
Topic::Sign { id, attempt, round } => match round {
|
||||||
SigningProtocolRound::Preprocess => None,
|
SigningProtocolRound::Preprocess => None,
|
||||||
SigningProtocolRound::Share => {
|
SigningProtocolRound::Share => {
|
||||||
@@ -170,7 +170,7 @@ impl Topic {
|
|||||||
}
|
}
|
||||||
SigningProtocolRound::Share => None,
|
SigningProtocolRound::Share => None,
|
||||||
},
|
},
|
||||||
Topic::SlashReport { .. } => None,
|
Topic::SlashReport => None,
|
||||||
Topic::Sign { id, attempt, round } => match round {
|
Topic::Sign { id, attempt, round } => match round {
|
||||||
SigningProtocolRound::Preprocess => {
|
SigningProtocolRound::Preprocess => {
|
||||||
Some(Topic::Sign { id, attempt, round: SigningProtocolRound::Share })
|
Some(Topic::Sign { id, attempt, round: SigningProtocolRound::Share })
|
||||||
@@ -189,7 +189,7 @@ impl Topic {
|
|||||||
// We don't require recognition for the first attempt, solely the re-attempts
|
// We don't require recognition for the first attempt, solely the re-attempts
|
||||||
Topic::DkgConfirmation { attempt, .. } => *attempt != 0,
|
Topic::DkgConfirmation { attempt, .. } => *attempt != 0,
|
||||||
// We don't require recognition for the slash report
|
// We don't require recognition for the slash report
|
||||||
Topic::SlashReport { .. } => false,
|
Topic::SlashReport => false,
|
||||||
// We do require recognition for every sign protocol
|
// We do require recognition for every sign protocol
|
||||||
Topic::Sign { .. } => true,
|
Topic::Sign { .. } => true,
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ impl Topic {
|
|||||||
match self {
|
match self {
|
||||||
Topic::RemoveParticipant { .. } => Participating::Everyone,
|
Topic::RemoveParticipant { .. } => Participating::Everyone,
|
||||||
Topic::DkgConfirmation { .. } => Participating::Participated,
|
Topic::DkgConfirmation { .. } => Participating::Participated,
|
||||||
Topic::SlashReport { .. } => Participating::Everyone,
|
Topic::SlashReport => Participating::Everyone,
|
||||||
Topic::Sign { .. } => Participating::Participated,
|
Topic::Sign { .. } => Participating::Participated,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,8 @@ fn weight<D: Send + Clone + SecureDigest, F: PrimeField>(digest: &mut DigestTran
|
|||||||
// Derive a scalar from enough bits of entropy that bias is < 2^128
|
// Derive a scalar from enough bits of entropy that bias is < 2^128
|
||||||
// This can't be const due to its usage of a generic
|
// This can't be const due to its usage of a generic
|
||||||
// Also due to the usize::try_from, yet that could be replaced with an `as`
|
// Also due to the usize::try_from, yet that could be replaced with an `as`
|
||||||
// The + 7 forces it to round up
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let BYTES: usize = usize::try_from(((F::NUM_BITS + 128) + 7) / 8).unwrap();
|
let BYTES: usize = usize::try_from((F::NUM_BITS + 128).div_ceil(8)).unwrap();
|
||||||
|
|
||||||
let mut remaining = BYTES;
|
let mut remaining = BYTES;
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ db-urls = ["https://github.com/rustsec/advisory-db"]
|
|||||||
yanked = "deny"
|
yanked = "deny"
|
||||||
|
|
||||||
ignore = [
|
ignore = [
|
||||||
"RUSTSEC-2020-0168", # mach is unmaintained
|
|
||||||
"RUSTSEC-2021-0139", # https://github.com/serai-dex/serai/228
|
|
||||||
"RUSTSEC-2022-0061", # https://github.com/serai-dex/serai/227
|
"RUSTSEC-2022-0061", # https://github.com/serai-dex/serai/227
|
||||||
"RUSTSEC-2024-0370", # proc-macro-error is unmaintained
|
"RUSTSEC-2024-0370", # proc-macro-error is unmaintained
|
||||||
"RUSTSEC-2024-0384", # instant is unmaintained
|
"RUSTSEC-2024-0384", # instant is unmaintained
|
||||||
@@ -123,7 +121,7 @@ wildcards = "warn"
|
|||||||
highlight = "all"
|
highlight = "all"
|
||||||
deny = [
|
deny = [
|
||||||
{ name = "serde_derive", version = ">=1.0.172, <1.0.185" },
|
{ name = "serde_derive", version = ">=1.0.172, <1.0.185" },
|
||||||
{ name = "hashbrown", version = ">=0.15" },
|
{ name = "hashbrown", version = "=0.15.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[sources]
|
[sources]
|
||||||
@@ -133,6 +131,6 @@ 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/rust-lang-nursery/lazy-static.rs",
|
||||||
"https://github.com/serai-dex/substrate-bip39",
|
"https://github.com/serai-dex/substrate-bip39",
|
||||||
"https://github.com/serai-dex/substrate",
|
"https://github.com/serai-dex/polkadot-sdk",
|
||||||
"https://github.com/kayabaNerve/pasta_curves",
|
"https://github.com/kayabaNerve/pasta_curves",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ serai-db = { path = "../common/db", optional = true }
|
|||||||
|
|
||||||
serai-env = { path = "../common/env" }
|
serai-env = { path = "../common/env" }
|
||||||
|
|
||||||
serai-primitives = { path = "../substrate/primitives", features = ["borsh"] }
|
serai-primitives = { path = "../substrate/primitives", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
parity-db = ["serai-db/parity-db"]
|
parity-db = ["serai-db/parity-db"]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ RUN rm -rf /etc/apt/sources.list.d/debian.sources && \
|
|||||||
RUN apt update && apt upgrade && apt install clang -y
|
RUN apt update && apt upgrade && apt install clang -y
|
||||||
|
|
||||||
# Add the wasm toolchain
|
# Add the wasm toolchain
|
||||||
RUN rustup target add wasm32-unknown-unknown
|
RUN rustup target add wasmv1-none
|
||||||
|
|
||||||
FROM deterministic
|
FROM deterministic
|
||||||
|
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ RUN apt install -y pkg-config clang
|
|||||||
RUN apt install -y make protobuf-compiler
|
RUN apt install -y make protobuf-compiler
|
||||||
|
|
||||||
# Add the wasm toolchain
|
# Add the wasm toolchain
|
||||||
RUN rustup target add wasm32-unknown-unknown
|
RUN rustup target add wasmv1-none
|
||||||
|
|
||||||
{prelude}
|
{prelude}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ rand_core = { version = "0.6", default-features = false, features = ["std", "get
|
|||||||
|
|
||||||
frost = { package = "modular-frost", path = "../../crypto/frost", version = "^0.8.1", default-features = false }
|
frost = { package = "modular-frost", path = "../../crypto/frost", version = "^0.8.1", default-features = false }
|
||||||
|
|
||||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }
|
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, fea
|
|||||||
dkg = { package = "dkg", path = "../../crypto/dkg", default-features = false, features = ["std", "evrf-ristretto"] }
|
dkg = { package = "dkg", path = "../../crypto/dkg", default-features = false, features = ["std", "evrf-ristretto"] }
|
||||||
|
|
||||||
# Substrate
|
# Substrate
|
||||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }
|
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
# Encoders
|
# Encoders
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
|
|||||||
|
|
||||||
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std", "borsh"] }
|
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std", "borsh"] }
|
||||||
|
|
||||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std", "borsh"] }
|
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||||
in-instructions-primitives = { package = "serai-in-instructions-primitives", path = "../../substrate/in-instructions/primitives", default-features = false, features = ["std", "borsh"] }
|
|
||||||
coins-primitives = { package = "serai-coins-primitives", path = "../../substrate/coins/primitives", default-features = false, features = ["std", "borsh"] }
|
|
||||||
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std", "borsh"] }
|
|
||||||
|
|
||||||
serai-cosign = { path = "../../coordinator/cosign", default-features = false }
|
serai-cosign = { path = "../../coordinator/cosign", default-features = false }
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
group = { version = "0.13", default-features = false }
|
group = { version = "0.13", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std", "borsh"] }
|
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||||
serai-coins-primitives = { path = "../../substrate/coins/primitives", default-features = false, features = ["std", "borsh"] }
|
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
|
||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
|
|||||||
@@ -36,9 +36,6 @@ serai-db = { path = "../../common/db" }
|
|||||||
messages = { package = "serai-processor-messages", path = "../messages" }
|
messages = { package = "serai-processor-messages", path = "../messages" }
|
||||||
|
|
||||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std", "borsh"] }
|
|
||||||
serai-in-instructions-primitives = { path = "../../substrate/in-instructions/primitives", default-features = false, features = ["std", "borsh"] }
|
|
||||||
serai-coins-primitives = { path = "../../substrate/coins/primitives", default-features = false, features = ["std", "borsh"] }
|
|
||||||
|
|
||||||
primitives = { package = "serai-processor-primitives", path = "../primitives" }
|
primitives = { package = "serai-processor-primitives", path = "../primitives" }
|
||||||
scheduler-primitives = { package = "serai-processor-scheduler-primitives", path = "../scheduler/primitives" }
|
scheduler-primitives = { package = "serai-processor-scheduler-primitives", path = "../scheduler/primitives" }
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals
|
|||||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||||
|
|
||||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }
|
|
||||||
serai-in-instructions-primitives = { path = "../../substrate/in-instructions/primitives", default-features = false, features = ["std"] }
|
|
||||||
|
|
||||||
serai-db = { path = "../../common/db" }
|
serai-db = { path = "../../common/db" }
|
||||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.81"
|
channel = "1.85"
|
||||||
targets = ["wasm32-unknown-unknown"]
|
targets = ["wasmv1-none"]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
components = ["rust-src", "rustfmt", "clippy"]
|
components = ["rustfmt", "clippy"]
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ brew install rustup
|
|||||||
```
|
```
|
||||||
rustup update
|
rustup update
|
||||||
rustup toolchain install stable
|
rustup toolchain install stable
|
||||||
rustup target add wasm32-unknown-unknown
|
rustup target add wasmv1-none
|
||||||
rustup toolchain install nightly
|
rustup toolchain install nightly
|
||||||
rustup target add wasm32-unknown-unknown --toolchain nightly
|
rustup target add wasmv1-none --toolchain nightly
|
||||||
```
|
```
|
||||||
|
|
||||||
### Install Solidity
|
### Install Solidity
|
||||||
|
|||||||
@@ -12,76 +12,41 @@ rust-version = "1.80"
|
|||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = ["serde"]
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
|
bitvec = { version = "1", default-features = false, features = ["alloc"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive", "bit-vec"] }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true }
|
||||||
|
scale-info = { version = "2", default-features = false, features = ["derive"], optional = true }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, features = ["serde"], optional = true }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, optional = true }
|
||||||
|
|
||||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-consensus-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }
|
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }
|
||||||
serai-coins-primitives = { path = "../coins/primitives", version = "0.1", default-features = false }
|
|
||||||
serai-validator-sets-primitives = { path = "../validator-sets/primitives", version = "0.1", default-features = false }
|
|
||||||
serai-genesis-liquidity-primitives = { path = "../genesis-liquidity/primitives", version = "0.1", default-features = false }
|
|
||||||
serai-emissions-primitives = { path = "../emissions/primitives", version = "0.1", default-features = false }
|
|
||||||
serai-in-instructions-primitives = { path = "../in-instructions/primitives", version = "0.1", default-features = false }
|
|
||||||
serai-signals-primitives = { path = "../signals/primitives", version = "0.1", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
|
"borsh/std",
|
||||||
|
|
||||||
"bitvec/std",
|
"bitvec/std",
|
||||||
|
|
||||||
"scale/std",
|
|
||||||
"scale-info/std",
|
|
||||||
|
|
||||||
"borsh?/std",
|
|
||||||
"serde?/std",
|
|
||||||
|
|
||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-runtime/std",
|
|
||||||
|
|
||||||
"sp-consensus-babe/std",
|
"serde?/std",
|
||||||
"sp-consensus-grandpa/std",
|
"scale?/std",
|
||||||
|
"scale-info?/std",
|
||||||
"frame-support/std",
|
"sp-runtime?/std",
|
||||||
|
"frame-support?/std",
|
||||||
|
|
||||||
"serai-primitives/std",
|
"serai-primitives/std",
|
||||||
"serai-coins-primitives/std",
|
|
||||||
"serai-validator-sets-primitives/std",
|
|
||||||
"serai-genesis-liquidity-primitives/std",
|
|
||||||
"serai-emissions-primitives/std",
|
|
||||||
"serai-in-instructions-primitives/std",
|
|
||||||
"serai-signals-primitives/std",
|
|
||||||
]
|
|
||||||
borsh = [
|
|
||||||
"dep:borsh",
|
|
||||||
"serai-primitives/borsh",
|
|
||||||
"serai-coins-primitives/borsh",
|
|
||||||
"serai-validator-sets-primitives/borsh",
|
|
||||||
"serai-genesis-liquidity-primitives/borsh",
|
|
||||||
"serai-in-instructions-primitives/borsh",
|
|
||||||
"serai-signals-primitives/borsh",
|
|
||||||
]
|
|
||||||
serde = [
|
|
||||||
"dep:serde",
|
|
||||||
"serai-primitives/serde",
|
|
||||||
"serai-coins-primitives/serde",
|
|
||||||
"serai-validator-sets-primitives/serde",
|
|
||||||
"serai-genesis-liquidity-primitives/serde",
|
|
||||||
"serai-in-instructions-primitives/serde",
|
|
||||||
"serai-signals-primitives/serde",
|
|
||||||
]
|
]
|
||||||
|
substrate = ["serde", "scale", "scale-info", "sp-runtime", "frame-support", "serai-primitives/non_canonical_scale_derivations"]
|
||||||
|
try-runtime = ["sp-runtime/try-runtime"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 Luke Parker
|
Copyright (c) 2023-2025 Luke Parker
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
4
substrate/abi/README.md
Normal file
4
substrate/abi/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# serai-abi
|
||||||
|
|
||||||
|
Serai's ABI, inclusive to the transaction, event, and block types. MIT-licensed to ensure usability
|
||||||
|
in a variety of contexts.
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
use sp_consensus_babe::EquivocationProof;
|
|
||||||
|
|
||||||
use serai_primitives::{Header, SeraiAddress};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
pub struct ReportEquivocation {
|
|
||||||
pub equivocation_proof: alloc::boxed::Box<EquivocationProof<Header>>,
|
|
||||||
pub key_owner_proof: SeraiAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
// We could define a Babe Config here and use the literal pallet_babe::Call
|
|
||||||
// The disadvantage to this would be the complexity and presence of junk fields such as `__Ignore`
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
pub enum Call {
|
|
||||||
report_equivocation(ReportEquivocation),
|
|
||||||
report_equivocation_unsigned(ReportEquivocation),
|
|
||||||
}
|
|
||||||
360
substrate/abi/src/block.rs
Normal file
360
substrate/abi/src/block.rs
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
primitives::{BlockHash, merkle::UnbalancedMerkleTree},
|
||||||
|
Transaction,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The tag for the hash of a transaction's event, forming a leaf of the Merkle tree of its events.
|
||||||
|
pub const TRANSACTION_EVENTS_COMMITMENT_LEAF_TAG: u8 = 0;
|
||||||
|
/// The tag for the branch hashes of transaction events.
|
||||||
|
pub const TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG: u8 = 1;
|
||||||
|
/// The tag for the hash of a transaction's hash and its events' Merkle root, forming a leaf of the
|
||||||
|
/// Merkle tree which is the events commitment.
|
||||||
|
pub const EVENTS_COMMITMENT_LEAF_TAG: u8 = 2;
|
||||||
|
/// The tag for for the branch hashes of the Merkle tree which is the events commitments.
|
||||||
|
pub const EVENTS_COMMITMENT_BRANCH_TAG: u8 = 3;
|
||||||
|
|
||||||
|
/// A V1 header for a block.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct HeaderV1 {
|
||||||
|
/// The index of this block on the blockchain.
|
||||||
|
///
|
||||||
|
/// The genesis block has number 0.
|
||||||
|
pub number: u64,
|
||||||
|
/// The commitment to the DAG this header builds upon.
|
||||||
|
///
|
||||||
|
/// This is defined as an unbalanced Merkle tree so light clients may sync one header per epoch,
|
||||||
|
/// and then may prove the inclusion of any header in logarithmic depth (without providing the
|
||||||
|
/// entire header chain).
|
||||||
|
///
|
||||||
|
/// Alternative popular options would be a Merkle Mountain Range, which makes more recent blocks
|
||||||
|
/// cheaper to prove at the sacrifice of older blocks being more expensive to prove. An MMR isn't
|
||||||
|
/// used in order to minimize the protocol's surface area. Additionally, even though the
|
||||||
|
/// unbalanced Merkle tree doesn't achieve such notably short paths for recent blocks, it does
|
||||||
|
/// inherently provide lower-depth paths to more recent items *on imbalance*.
|
||||||
|
pub builds_upon: UnbalancedMerkleTree,
|
||||||
|
/// The UNIX time in milliseconds this block was created at.
|
||||||
|
pub unix_time_in_millis: u64,
|
||||||
|
/// The commitment to the transactions within this block.
|
||||||
|
pub transactions_commitment: UnbalancedMerkleTree,
|
||||||
|
/// The commitment to the events within this block.
|
||||||
|
///
|
||||||
|
/// The leaves of this tree will be of the form
|
||||||
|
/// `(EVENTS_COMMITMENT_LEAF_TAG, transaction hash, transaction's events' Merkle tree root)`.
|
||||||
|
/// A transaction may have the same event multiple times, yet an event may be uniquely identified
|
||||||
|
/// by its path within the tree.
|
||||||
|
pub events_commitment: UnbalancedMerkleTree,
|
||||||
|
/// A commitment to the consensus data used to justify adding this block to the blockchain.
|
||||||
|
pub consensus_commitment: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A header for a block.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub enum Header {
|
||||||
|
/// A version 1 header.
|
||||||
|
V1(HeaderV1),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
/// Get the hash of the header.
|
||||||
|
pub fn number(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Header::V1(HeaderV1 { number, .. }) => *number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get the commitment to the DAG this header builds upon.
|
||||||
|
pub fn builds_upon(&self) -> UnbalancedMerkleTree {
|
||||||
|
match self {
|
||||||
|
Header::V1(HeaderV1 { builds_upon, .. }) => *builds_upon,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// The commitment to the transactions within this block.
|
||||||
|
pub fn transactions_commitment(&self) -> UnbalancedMerkleTree {
|
||||||
|
match self {
|
||||||
|
Header::V1(HeaderV1 { transactions_commitment, .. }) => *transactions_commitment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// The commitment to the events within this block.
|
||||||
|
pub fn events_commitment(&self) -> UnbalancedMerkleTree {
|
||||||
|
match self {
|
||||||
|
Header::V1(HeaderV1 { events_commitment, .. }) => *events_commitment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get the hash of the header.
|
||||||
|
pub fn hash(&self) -> BlockHash {
|
||||||
|
BlockHash(sp_core::blake2_256(&borsh::to_vec(self).unwrap()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A block.
|
||||||
|
///
|
||||||
|
/// This does not guarantee consistency. The header's `transactions_root` may not match the
|
||||||
|
/// contained transactions.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct Block {
|
||||||
|
/// The block's header.
|
||||||
|
pub header: Header,
|
||||||
|
/// The block's transactions.
|
||||||
|
pub transactions: Vec<Transaction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "substrate")]
|
||||||
|
mod substrate {
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use scale::{Encode, Decode};
|
||||||
|
use scale_info::TypeInfo;
|
||||||
|
|
||||||
|
use sp_core::H256;
|
||||||
|
use sp_runtime::{
|
||||||
|
generic::{DigestItem, Digest},
|
||||||
|
traits::{Header as HeaderTrait, HeaderProvider, Block as BlockTrait},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// The digest for all of the Serai-specific header fields added before execution of the block.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct SeraiPreExecutionDigest {
|
||||||
|
/// The UNIX time in milliseconds this block was created at.
|
||||||
|
pub unix_time_in_millis: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The digest for all of the Serai-specific header fields determined during execution of the
|
||||||
|
/// block.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct SeraiExecutionDigest {
|
||||||
|
/// The commitment to the DAG this header builds upon.
|
||||||
|
pub builds_upon: UnbalancedMerkleTree,
|
||||||
|
/// The commitment to the transactions within this block.
|
||||||
|
pub transactions_commitment: UnbalancedMerkleTree,
|
||||||
|
/// The commitment to the events within this block.
|
||||||
|
pub events_commitment: UnbalancedMerkleTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeraiPreExecutionDigest {
|
||||||
|
/// The consensus ID for a Serai pre-execution digest.
|
||||||
|
pub const CONSENSUS_ID: [u8; 4] = *b"SRIP";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeraiExecutionDigest {
|
||||||
|
/// The consensus ID for a Serai execution digest.
|
||||||
|
pub const CONSENSUS_ID: [u8; 4] = *b"SRIE";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The consensus data for a V1 header.
|
||||||
|
///
|
||||||
|
/// This is not considered part of the protocol proper and may be pruned in the future. It's
|
||||||
|
/// solely considered used for consensus now.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||||
|
pub struct ConsensusV1 {
|
||||||
|
/// The hash of the immediately preceding block.
|
||||||
|
parent_hash: H256,
|
||||||
|
/// The root for the Merkle tree of transactions, as defined by Substrate.
|
||||||
|
///
|
||||||
|
/// The format of this differs from Serai's format for the commitment to the transactions.
|
||||||
|
transactions_root: H256,
|
||||||
|
/// The state root.
|
||||||
|
state_root: H256,
|
||||||
|
/// The consensus digests.
|
||||||
|
digest: Digest,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A V1 header for a block, as needed by Substrate.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||||
|
pub struct SubstrateHeaderV1 {
|
||||||
|
number: u64,
|
||||||
|
consensus: ConsensusV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A header for a block, as needed by Substrate.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||||
|
pub enum SubstrateHeader {
|
||||||
|
/// A version 1 header.
|
||||||
|
V1(SubstrateHeaderV1),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&SubstrateHeader> for Header {
|
||||||
|
fn from(header: &SubstrateHeader) -> Self {
|
||||||
|
match header {
|
||||||
|
SubstrateHeader::V1(header) => {
|
||||||
|
let mut pre_execution_digest = None;
|
||||||
|
let mut execution_digest = None;
|
||||||
|
for log in header.consensus.digest.logs() {
|
||||||
|
match log {
|
||||||
|
DigestItem::PreRuntime(consensus, encoded)
|
||||||
|
if *consensus == SeraiExecutionDigest::CONSENSUS_ID =>
|
||||||
|
{
|
||||||
|
pre_execution_digest =
|
||||||
|
SeraiPreExecutionDigest::deserialize_reader(&mut encoded.as_slice()).ok();
|
||||||
|
}
|
||||||
|
DigestItem::Consensus(consensus, encoded)
|
||||||
|
if *consensus == SeraiExecutionDigest::CONSENSUS_ID =>
|
||||||
|
{
|
||||||
|
execution_digest =
|
||||||
|
SeraiExecutionDigest::deserialize_reader(&mut encoded.as_slice()).ok();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Header::V1(HeaderV1 {
|
||||||
|
number: header.number,
|
||||||
|
builds_upon: execution_digest
|
||||||
|
.as_ref()
|
||||||
|
.map(|digest| digest.builds_upon)
|
||||||
|
.unwrap_or(UnbalancedMerkleTree::EMPTY),
|
||||||
|
unix_time_in_millis: pre_execution_digest
|
||||||
|
.as_ref()
|
||||||
|
.map(|digest| digest.unix_time_in_millis)
|
||||||
|
.unwrap_or(0),
|
||||||
|
transactions_commitment: execution_digest
|
||||||
|
.as_ref()
|
||||||
|
.map(|digest| digest.transactions_commitment)
|
||||||
|
.unwrap_or(UnbalancedMerkleTree::EMPTY),
|
||||||
|
events_commitment: execution_digest
|
||||||
|
.as_ref()
|
||||||
|
.map(|digest| digest.events_commitment)
|
||||||
|
.unwrap_or(UnbalancedMerkleTree::EMPTY),
|
||||||
|
consensus_commitment: sp_core::blake2_256(&header.consensus.encode()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A block, as needed by Substrate.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, sp_runtime::Serialize)]
|
||||||
|
pub struct SubstrateBlock {
|
||||||
|
header: SubstrateHeader,
|
||||||
|
#[serde(skip)] // This makes this unsafe to deserialize, but we don't impl `Deserialize`
|
||||||
|
transactions: Vec<Transaction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeaderTrait for SubstrateHeader {
|
||||||
|
type Number = u64;
|
||||||
|
type Hash = H256;
|
||||||
|
type Hashing = sp_runtime::traits::BlakeTwo256;
|
||||||
|
|
||||||
|
fn new(
|
||||||
|
number: Self::Number,
|
||||||
|
extrinsics_root: Self::Hash,
|
||||||
|
state_root: Self::Hash,
|
||||||
|
parent_hash: Self::Hash,
|
||||||
|
digest: Digest,
|
||||||
|
) -> Self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 {
|
||||||
|
number,
|
||||||
|
consensus: ConsensusV1 {
|
||||||
|
parent_hash,
|
||||||
|
transactions_root: extrinsics_root,
|
||||||
|
state_root,
|
||||||
|
digest,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn number(&self) -> &Self::Number {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { number, .. }) => number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_number(&mut self, number: Self::Number) {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { number: existing, .. }) => {
|
||||||
|
*existing = number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extrinsics_root(&self) -> &Self::Hash {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.transactions_root,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_extrinsics_root(&mut self, extrinsics_root: Self::Hash) {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => {
|
||||||
|
consensus.transactions_root = extrinsics_root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_root(&self) -> &Self::Hash {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.state_root,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_state_root(&mut self, state_root: Self::Hash) {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => {
|
||||||
|
consensus.state_root = state_root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent_hash(&self) -> &Self::Hash {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.parent_hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_parent_hash(&mut self, parent_hash: Self::Hash) {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => {
|
||||||
|
consensus.parent_hash = parent_hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn digest(&self) -> &Digest {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.digest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn digest_mut(&mut self) -> &mut Digest {
|
||||||
|
match self {
|
||||||
|
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &mut consensus.digest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash(&self) -> H256 {
|
||||||
|
H256::from(Header::from(self).hash().0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeaderProvider for SubstrateBlock {
|
||||||
|
type HeaderT = SubstrateHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockTrait for SubstrateBlock {
|
||||||
|
type Extrinsic = Transaction;
|
||||||
|
type Header = SubstrateHeader;
|
||||||
|
type Hash = H256;
|
||||||
|
fn header(&self) -> &Self::Header {
|
||||||
|
&self.header
|
||||||
|
}
|
||||||
|
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||||
|
&self.transactions
|
||||||
|
}
|
||||||
|
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||||
|
(self.header, self.transactions)
|
||||||
|
}
|
||||||
|
fn new(header: Self::Header, transactions: Vec<Self::Extrinsic>) -> Self {
|
||||||
|
Self { header, transactions }
|
||||||
|
}
|
||||||
|
fn encode_from(header: &Self::Header, transactions: &[Self::Extrinsic]) -> Vec<u8> {
|
||||||
|
let header = header.encode();
|
||||||
|
let transactions = transactions.encode();
|
||||||
|
let mut block = header;
|
||||||
|
block.extend(transactions);
|
||||||
|
block
|
||||||
|
}
|
||||||
|
fn hash(&self) -> Self::Hash {
|
||||||
|
self.header.hash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "substrate")]
|
||||||
|
pub use substrate::*;
|
||||||
@@ -1,25 +1,70 @@
|
|||||||
use serai_primitives::{Balance, SeraiAddress};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
pub use serai_coins_primitives as primitives;
|
use serai_primitives::{
|
||||||
use primitives::OutInstructionWithBalance;
|
address::SeraiAddress, balance::Balance, instructions::OutInstructionWithBalance,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
/// A call to coins.
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub enum Call {
|
pub enum Call {
|
||||||
transfer { to: SeraiAddress, balance: Balance },
|
/// Transfer these coins to the specified address.
|
||||||
burn { balance: Balance },
|
transfer {
|
||||||
burn_with_instruction { instruction: OutInstructionWithBalance },
|
/// The address to transfer to.
|
||||||
|
to: SeraiAddress,
|
||||||
|
/// The coins to transfer.
|
||||||
|
coins: Balance,
|
||||||
|
},
|
||||||
|
/// Burn these coins.
|
||||||
|
burn {
|
||||||
|
/// The coins to burn.
|
||||||
|
coins: Balance,
|
||||||
|
},
|
||||||
|
/// Burn these coins with an `OutInstruction` specified.
|
||||||
|
burn_with_instruction {
|
||||||
|
/// The `OutInstruction`, with the coins to burn.
|
||||||
|
instruction: OutInstructionWithBalance,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
impl Call {
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
pub(crate) fn is_signed(&self) -> bool {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
match self {
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
Call::transfer { .. } | Call::burn { .. } | Call::burn_with_instruction { .. } => true,
|
||||||
pub enum Event {
|
}
|
||||||
Mint { to: SeraiAddress, balance: Balance },
|
}
|
||||||
Burn { from: SeraiAddress, balance: Balance },
|
}
|
||||||
BurnWithInstruction { from: SeraiAddress, instruction: OutInstructionWithBalance },
|
|
||||||
Transfer { from: SeraiAddress, to: SeraiAddress, balance: Balance },
|
/// An event from the system.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub enum Event {
|
||||||
|
/// The specified coins were minted.
|
||||||
|
Mint {
|
||||||
|
/// The address minted to.
|
||||||
|
to: SeraiAddress,
|
||||||
|
/// The coins minted.
|
||||||
|
coins: Balance,
|
||||||
|
},
|
||||||
|
/// The specified coins were burnt.
|
||||||
|
Burn {
|
||||||
|
/// The address burnt from.
|
||||||
|
from: SeraiAddress,
|
||||||
|
/// The coins burnt.
|
||||||
|
coins: Balance,
|
||||||
|
},
|
||||||
|
/// The specified coins were burnt with an `OutInstruction` specified.
|
||||||
|
BurnWithInstruction {
|
||||||
|
/// The address burnt from.
|
||||||
|
from: SeraiAddress,
|
||||||
|
/// The `OutInstruction` specified, and the coins burnt.
|
||||||
|
instruction: OutInstructionWithBalance,
|
||||||
|
},
|
||||||
|
/// The specified coins were transferred.
|
||||||
|
Transfer {
|
||||||
|
/// The address transferred from.
|
||||||
|
from: SeraiAddress,
|
||||||
|
/// The address transferred to.
|
||||||
|
to: SeraiAddress,
|
||||||
|
/// The coins transferred.
|
||||||
|
coins: Balance,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,121 @@
|
|||||||
use sp_runtime::BoundedVec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use serai_primitives::*;
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
type PoolId = ExternalCoin;
|
use serai_primitives::{
|
||||||
type MaxSwapPathLength = sp_core::ConstU32<3>;
|
address::SeraiAddress,
|
||||||
|
coin::ExternalCoin,
|
||||||
|
balance::{Amount, ExternalBalance, Balance},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
/// A call to the DEX.
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub enum Call {
|
pub enum Call {
|
||||||
|
/// Add liquidity.
|
||||||
add_liquidity {
|
add_liquidity {
|
||||||
|
/// The coin to add liquidity for.
|
||||||
coin: ExternalCoin,
|
coin: ExternalCoin,
|
||||||
coin_desired: SubstrateAmount,
|
/// The intended amount of SRI to add as liquidity.
|
||||||
sri_desired: SubstrateAmount,
|
sri_intended: Amount,
|
||||||
coin_min: SubstrateAmount,
|
/// The intended amount of the coin to add as liquidity.
|
||||||
sri_min: SubstrateAmount,
|
coin_intended: Amount,
|
||||||
mint_to: SeraiAddress,
|
/// The minimum amount of SRI to add as liquidity.
|
||||||
|
sri_minimum: Amount,
|
||||||
|
/// The minimum amount of the coin to add as liquidity.
|
||||||
|
coin_minimum: Amount,
|
||||||
},
|
},
|
||||||
|
/// Transfer these liquidity tokens to the specified address.
|
||||||
|
transfer_liquidity {
|
||||||
|
/// The address to transfer to.
|
||||||
|
to: SeraiAddress,
|
||||||
|
/// The liquidity tokens to transfer.
|
||||||
|
liquidity_tokens: ExternalBalance,
|
||||||
|
},
|
||||||
|
/// Remove liquidity.
|
||||||
remove_liquidity {
|
remove_liquidity {
|
||||||
coin: ExternalCoin,
|
/// The liquidity tokens to burn, removing the underlying liquidity from the pool.
|
||||||
lp_token_burn: SubstrateAmount,
|
///
|
||||||
coin_min_receive: SubstrateAmount,
|
/// The `coin` within the balance is the coin to remove liquidity for.
|
||||||
sri_min_receive: SubstrateAmount,
|
liquidity_tokens: ExternalBalance,
|
||||||
withdraw_to: SeraiAddress,
|
/// The minimum amount of SRI to receive.
|
||||||
|
sri_minimum: Amount,
|
||||||
|
/// The minimum amount of the coin to receive.
|
||||||
|
coin_minimum: Amount,
|
||||||
},
|
},
|
||||||
swap_exact_tokens_for_tokens {
|
/// Swap an exact amount of coins.
|
||||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
swap_exact {
|
||||||
amount_in: SubstrateAmount,
|
/// The coins to swap.
|
||||||
amount_out_min: SubstrateAmount,
|
coins_to_swap: Balance,
|
||||||
send_to: SeraiAddress,
|
/// The minimum balance to receive.
|
||||||
|
minimum_to_receive: Balance,
|
||||||
},
|
},
|
||||||
swap_tokens_for_exact_tokens {
|
/// Swap for an exact amount of coins.
|
||||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
swap_for_exact {
|
||||||
amount_out: SubstrateAmount,
|
/// The coins to receive.
|
||||||
amount_in_max: SubstrateAmount,
|
coins_to_receive: Balance,
|
||||||
send_to: SeraiAddress,
|
/// The maximum amount to swap.
|
||||||
|
maximum_to_swap: Balance,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
impl Call {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
pub(crate) fn is_signed(&self) -> bool {
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
match self {
|
||||||
|
Call::add_liquidity { .. } |
|
||||||
|
Call::transfer_liquidity { .. } |
|
||||||
|
Call::remove_liquidity { .. } |
|
||||||
|
Call::swap_exact { .. } |
|
||||||
|
Call::swap_for_exact { .. } => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event from the DEX.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
PoolCreated {
|
/// Liquidity was added to a pool.
|
||||||
pool_id: PoolId,
|
|
||||||
pool_account: SeraiAddress,
|
|
||||||
},
|
|
||||||
|
|
||||||
LiquidityAdded {
|
LiquidityAdded {
|
||||||
who: SeraiAddress,
|
/// The account which added the liquidity.
|
||||||
mint_to: SeraiAddress,
|
origin: SeraiAddress,
|
||||||
pool_id: PoolId,
|
/// The account which received the liquidity tokens.
|
||||||
coin_amount: SubstrateAmount,
|
recipient: SeraiAddress,
|
||||||
sri_amount: SubstrateAmount,
|
/// The pool liquidity was added to.
|
||||||
lp_token_minted: SubstrateAmount,
|
pool: ExternalCoin,
|
||||||
|
/// The amount of liquidity tokens which were minted.
|
||||||
|
liquidity_tokens_minted: Amount,
|
||||||
|
/// The amount of the coin which was added to the pool's liquidity.
|
||||||
|
coin_amount: Amount,
|
||||||
|
/// The amount of SRI which was added to the pool's liquidity.
|
||||||
|
sri_amount: Amount,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Liquidity was removed from a pool.
|
||||||
LiquidityRemoved {
|
LiquidityRemoved {
|
||||||
who: SeraiAddress,
|
/// The account which removed the liquidity.
|
||||||
withdraw_to: SeraiAddress,
|
origin: SeraiAddress,
|
||||||
pool_id: PoolId,
|
/// The pool liquidity was removed from.
|
||||||
coin_amount: SubstrateAmount,
|
pool: ExternalCoin,
|
||||||
sri_amount: SubstrateAmount,
|
/// The mount of liquidity tokens which were burnt.
|
||||||
lp_token_burned: SubstrateAmount,
|
liquidity_tokens_burnt: Amount,
|
||||||
|
/// The amount of the coin which was removed from the pool's liquidity.
|
||||||
|
coin_amount: Amount,
|
||||||
|
/// The amount of SRI which was removed from the pool's liquidity.
|
||||||
|
sri_amount: Amount,
|
||||||
},
|
},
|
||||||
|
|
||||||
SwapExecuted {
|
/// A swap through the liquidity pools occurred.
|
||||||
who: SeraiAddress,
|
Swap {
|
||||||
send_to: SeraiAddress,
|
/// The account which made the swap.
|
||||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
origin: SeraiAddress,
|
||||||
amount_in: SubstrateAmount,
|
/// The recipient for the output of the swap.
|
||||||
amount_out: SubstrateAmount,
|
recipient: SeraiAddress,
|
||||||
|
/// The deltas incurred by the pools.
|
||||||
|
///
|
||||||
|
/// For a swap of sriABC to sriDEF, this would be
|
||||||
|
/// `[Balance { sriABC, 1 }, Balance { SRI, 2 }, Balance { sriDEF, 3 }]`, where
|
||||||
|
/// `Balance { sriABC, 1 }` was added to the `sriABC-SRI` pool, `Balance { SRI, 2 }` was
|
||||||
|
/// removed from the `sriABC-SRI` pool and added to the `sriDEF-SRI` pool, and
|
||||||
|
/// `Balance { sriDEF, 3 }` was removed from the `sriDEF-SRI` pool.
|
||||||
|
deltas: Vec<Balance>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
use serai_primitives::ExternalNetworkId;
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
use serai_primitives::network_id::ExternalNetworkId;
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
/// An event from economic security.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
EconomicSecurityReached { network: ExternalNetworkId },
|
/// Economic security was achieved for a network's validator set.
|
||||||
|
EconomicSecurityAchieved {
|
||||||
|
/// The network whose validator set achieved economic security.
|
||||||
|
network: ExternalNetworkId,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
pub use serai_emissions_primitives as primitives;
|
|
||||||
@@ -1,20 +1,50 @@
|
|||||||
pub use serai_genesis_liquidity_primitives as primitives;
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use serai_primitives::*;
|
use serai_primitives::{
|
||||||
use primitives::*;
|
crypto::Signature, address::SeraiAddress, balance::ExternalBalance, genesis::GenesisValues,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
/// A call to the genesis liquidity.
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum Call {
|
pub enum Call {
|
||||||
remove_coin_liquidity { balance: ExternalBalance },
|
/// Oraclize the value of non-Bitcoin external coins relative to Bitcoin.
|
||||||
oraclize_values { values: Values, signature: Signature },
|
oraclize_values {
|
||||||
|
/// The values of the non-Bitcoin external coins.
|
||||||
|
values: GenesisValues,
|
||||||
|
/// The signature by the genesis validators for these values.
|
||||||
|
signature: Signature,
|
||||||
|
},
|
||||||
|
/// Remove liquidity.
|
||||||
|
remove_liquidity {
|
||||||
|
/// The genesis liquidity to remove.
|
||||||
|
balance: ExternalBalance,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
impl Call {
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
pub(crate) fn is_signed(&self) -> bool {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
match self {
|
||||||
pub enum Event {
|
Call::oraclize_values { .. } => false,
|
||||||
GenesisLiquidityAdded { by: SeraiAddress, balance: ExternalBalance },
|
Call::remove_liquidity { .. } => true,
|
||||||
GenesisLiquidityRemoved { by: SeraiAddress, balance: ExternalBalance },
|
}
|
||||||
GenesisLiquidityAddedToPool { coin: ExternalBalance, sri: Amount },
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event from the genesis liquidity.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub enum Event {
|
||||||
|
/// Genesis liquidity added.
|
||||||
|
GenesisLiquidityAdded {
|
||||||
|
/// The recipient of the genesis liquidity.
|
||||||
|
recipient: SeraiAddress,
|
||||||
|
/// The coins added as genesis liquidity.
|
||||||
|
balance: ExternalBalance,
|
||||||
|
},
|
||||||
|
/// Genesis liquidity removed.
|
||||||
|
GenesisLiquidityRemoved {
|
||||||
|
/// The account which removed the genesis liquidity.
|
||||||
|
origin: SeraiAddress,
|
||||||
|
/// The amount of genesis liquidity removed.
|
||||||
|
balance: ExternalBalance,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
use sp_consensus_grandpa::EquivocationProof;
|
|
||||||
|
|
||||||
use serai_primitives::{BlockNumber, SeraiAddress};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
pub struct ReportEquivocation {
|
|
||||||
pub equivocation_proof: alloc::boxed::Box<EquivocationProof<[u8; 32], BlockNumber>>,
|
|
||||||
pub key_owner_proof: SeraiAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
pub enum Call {
|
|
||||||
report_equivocation(ReportEquivocation),
|
|
||||||
report_equivocation_unsigned(ReportEquivocation),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub enum Event {
|
|
||||||
NewAuthorities { authority_set: alloc::vec::Vec<(SeraiAddress, u64)> },
|
|
||||||
// TODO: Remove these
|
|
||||||
Paused,
|
|
||||||
Resumed,
|
|
||||||
}
|
|
||||||
@@ -1,30 +1,47 @@
|
|||||||
use serai_primitives::*;
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
pub use serai_in_instructions_primitives as primitives;
|
use serai_primitives::{
|
||||||
use primitives::SignedBatch;
|
BlockHash, network_id::ExternalNetworkId, validator_sets::Session, instructions::SignedBatch,
|
||||||
use serai_validator_sets_primitives::Session;
|
};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
/// A call to `InInstruction`s.
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub enum Call {
|
pub enum Call {
|
||||||
execute_batch { batch: SignedBatch },
|
/// Execute a batch of `InInstruction`s.
|
||||||
|
execute_batch {
|
||||||
|
/// The batch to execute.
|
||||||
|
batch: SignedBatch,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
impl Call {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
pub(crate) fn is_signed(&self) -> bool {
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
match self {
|
||||||
|
Call::execute_batch { .. } => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event from `InInstruction`s.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
/// A batch of `InInstruction`s was executed.
|
||||||
Batch {
|
Batch {
|
||||||
|
/// The network for which a batch was executed.
|
||||||
network: ExternalNetworkId,
|
network: ExternalNetworkId,
|
||||||
|
/// The session which published the batch.
|
||||||
publishing_session: Session,
|
publishing_session: Session,
|
||||||
|
/// The ID of the batch.
|
||||||
id: u32,
|
id: u32,
|
||||||
|
/// The hash of the block on the external network which caused this batch's creation.
|
||||||
external_network_block_hash: BlockHash,
|
external_network_block_hash: BlockHash,
|
||||||
|
/// The hash of the `InInstruction`s within this batch.
|
||||||
in_instructions_hash: [u8; 32],
|
in_instructions_hash: [u8; 32],
|
||||||
|
/// The results of each `InInstruction` within the batch.
|
||||||
|
#[borsh(
|
||||||
|
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bitvec",
|
||||||
|
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bitvec"
|
||||||
|
)]
|
||||||
in_instruction_results: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
in_instruction_results: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||||
},
|
},
|
||||||
Halt {
|
|
||||||
network: ExternalNetworkId,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,94 +1,98 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![deny(missing_docs)]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
pub use serai_primitives as primitives;
|
pub use serai_primitives as primitives;
|
||||||
|
|
||||||
|
/// Call/Event for the system.
|
||||||
pub mod system;
|
pub mod system;
|
||||||
|
|
||||||
pub mod timestamp;
|
/// Call/Event for coins.
|
||||||
|
|
||||||
pub mod coins;
|
pub mod coins;
|
||||||
pub mod liquidity_tokens;
|
|
||||||
pub mod dex;
|
|
||||||
|
|
||||||
|
/// Call/Event for validator sets.
|
||||||
pub mod validator_sets;
|
pub mod validator_sets;
|
||||||
|
/// Call/Event for signals.
|
||||||
pub mod genesis_liquidity;
|
|
||||||
pub mod emissions;
|
|
||||||
|
|
||||||
pub mod economic_security;
|
|
||||||
|
|
||||||
pub mod in_instructions;
|
|
||||||
|
|
||||||
pub mod signals;
|
pub mod signals;
|
||||||
|
|
||||||
pub mod babe;
|
/// Call/Event for the DEX.
|
||||||
pub mod grandpa;
|
pub mod dex;
|
||||||
|
|
||||||
pub mod tx;
|
/// Call/Event for genesis liquidity.
|
||||||
|
pub mod genesis_liquidity;
|
||||||
|
/// Event for economic security.
|
||||||
|
pub mod economic_security;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
/// Call/Event for `InInstruction`s.
|
||||||
|
pub mod in_instructions;
|
||||||
|
|
||||||
|
mod transaction;
|
||||||
|
pub use transaction::*;
|
||||||
|
|
||||||
|
mod block;
|
||||||
|
pub use block::*;
|
||||||
|
|
||||||
|
/// All calls.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
#[borsh(use_discriminant = true)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum Call {
|
pub enum Call {
|
||||||
Timestamp(timestamp::Call),
|
// The call for the system.
|
||||||
Coins(coins::Call),
|
// System(system::Call) = 0,
|
||||||
LiquidityTokens(liquidity_tokens::Call),
|
/// The call for coins.
|
||||||
Dex(dex::Call),
|
Coins(coins::Call) = 1,
|
||||||
ValidatorSets(validator_sets::Call),
|
/// The call for validator sets.
|
||||||
GenesisLiquidity(genesis_liquidity::Call),
|
ValidatorSets(validator_sets::Call) = 2,
|
||||||
InInstructions(in_instructions::Call),
|
/// The call for signals.
|
||||||
Signals(signals::Call),
|
Signals(signals::Call) = 3,
|
||||||
Babe(babe::Call),
|
/// The call for the DEX.
|
||||||
Grandpa(grandpa::Call),
|
Dex(dex::Call) = 4,
|
||||||
|
/// The call for genesis liquidity.
|
||||||
|
GenesisLiquidity(genesis_liquidity::Call) = 5,
|
||||||
|
// The call for economic security.
|
||||||
|
// EconomicSecurity = 6,
|
||||||
|
/// The call for `InInstruction`s.
|
||||||
|
InInstructions(in_instructions::Call) = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this
|
impl Call {
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
pub(crate) fn is_signed(&self) -> bool {
|
||||||
pub enum TransactionPaymentEvent {
|
match self {
|
||||||
TransactionFeePaid { who: serai_primitives::SeraiAddress, actual_fee: u64, tip: u64 },
|
Call::Coins(call) => call.is_signed(),
|
||||||
|
Call::ValidatorSets(call) => call.is_signed(),
|
||||||
|
Call::Signals(call) => call.is_signed(),
|
||||||
|
Call::Dex(call) => call.is_signed(),
|
||||||
|
Call::GenesisLiquidity(call) => call.is_signed(),
|
||||||
|
Call::InInstructions(call) => call.is_signed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
/// All events.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
#[borsh(use_discriminant = true)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
System(system::Event),
|
/// The event for the system.
|
||||||
Timestamp,
|
System(system::Event) = 0,
|
||||||
TransactionPayment(TransactionPaymentEvent),
|
/// The event for coins.
|
||||||
Coins(coins::Event),
|
Coins(coins::Event) = 1,
|
||||||
LiquidityTokens(liquidity_tokens::Event),
|
/// The event for validator sets.
|
||||||
Dex(dex::Event),
|
ValidatorSets(validator_sets::Event) = 2,
|
||||||
ValidatorSets(validator_sets::Event),
|
/// The event for signals.
|
||||||
GenesisLiquidity(genesis_liquidity::Event),
|
Signals(signals::Event) = 3,
|
||||||
Emissions,
|
/// The event for the DEX.
|
||||||
EconomicSecurity(economic_security::Event),
|
Dex(dex::Event) = 4,
|
||||||
InInstructions(in_instructions::Event),
|
/// The event for genesis liquidity.
|
||||||
Signals(signals::Event),
|
GenesisLiquidity(genesis_liquidity::Event) = 5,
|
||||||
Babe,
|
/// The event for economic security.
|
||||||
Grandpa(grandpa::Event),
|
EconomicSecurity(economic_security::Event) = 6,
|
||||||
|
/// The event for `InInstruction`s.
|
||||||
|
InInstructions(in_instructions::Event) = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub struct Extra {
|
|
||||||
pub era: sp_runtime::generic::Era,
|
|
||||||
#[codec(compact)]
|
|
||||||
pub nonce: u32,
|
|
||||||
#[codec(compact)]
|
|
||||||
pub tip: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub struct SignedPayloadExtra {
|
|
||||||
pub spec_version: u32,
|
|
||||||
pub tx_version: u32,
|
|
||||||
pub genesis: [u8; 32],
|
|
||||||
pub mortality_checkpoint: [u8; 32],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Transaction = tx::Transaction<Call, Extra>;
|
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
use serai_primitives::{Balance, SeraiAddress};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub enum Call {
|
|
||||||
burn { balance: Balance },
|
|
||||||
transfer { to: SeraiAddress, balance: Balance },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub enum Event {
|
|
||||||
Mint { to: SeraiAddress, balance: Balance },
|
|
||||||
Burn { from: SeraiAddress, balance: Balance },
|
|
||||||
Transfer { from: SeraiAddress, to: SeraiAddress, balance: Balance },
|
|
||||||
}
|
|
||||||
@@ -1,59 +1,132 @@
|
|||||||
use serai_primitives::{NetworkId, SeraiAddress};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use serai_validator_sets_primitives::ValidatorSet;
|
use serai_primitives::{
|
||||||
|
address::SeraiAddress, network_id::NetworkId, validator_sets::ValidatorSet, signals::Signal,
|
||||||
|
};
|
||||||
|
|
||||||
pub use serai_signals_primitives as primitives;
|
/// A call to signals.
|
||||||
use primitives::SignalId;
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub enum Call {
|
pub enum Call {
|
||||||
register_retirement_signal { in_favor_of: [u8; 32] },
|
/// Register a retirement signal.
|
||||||
revoke_retirement_signal { retirement_signal_id: [u8; 32] },
|
register_retirement_signal {
|
||||||
favor { signal_id: SignalId, for_network: NetworkId },
|
/// The protocol favored over the current protocol.
|
||||||
revoke_favor { signal_id: SignalId, for_network: NetworkId },
|
in_favor_of: [u8; 32],
|
||||||
stand_against { signal_id: SignalId, for_network: NetworkId },
|
},
|
||||||
|
/// Revoke a retirement signal.
|
||||||
|
revoke_retirement_signal {
|
||||||
|
/// The protocol which was favored over the current protocol
|
||||||
|
was_in_favor_of: [u8; 32],
|
||||||
|
},
|
||||||
|
/// Favor a signal.
|
||||||
|
favor {
|
||||||
|
/// The signal to favor.
|
||||||
|
signal: Signal,
|
||||||
|
/// The network this validator is expressing favor with.
|
||||||
|
///
|
||||||
|
/// A validator may be an active validator for multiple networks. The validator must specify
|
||||||
|
/// which network they're expressing favor with in this call.
|
||||||
|
with_network: NetworkId,
|
||||||
|
},
|
||||||
|
/// Revoke favor for a signal.
|
||||||
|
revoke_favor {
|
||||||
|
/// The signal to revoke favor for.
|
||||||
|
signal: Signal,
|
||||||
|
/// The network this validator is revoking favor with.
|
||||||
|
///
|
||||||
|
/// A validator may have expressed favor with multiple networks. The validator must specify
|
||||||
|
/// which network they're revoking favor with in this call.
|
||||||
|
with_network: NetworkId,
|
||||||
|
},
|
||||||
|
/// Stand against a signal.
|
||||||
|
///
|
||||||
|
/// This has no effects other than emitting an event that this signal is stood against. If the
|
||||||
|
/// origin has prior expressed favor, they must still call `revoke_favor` for each network they
|
||||||
|
/// expressed favor with.
|
||||||
|
stand_against {
|
||||||
|
/// The signal to stand against.
|
||||||
|
signal: Signal,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
impl Call {
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
pub(crate) fn is_signed(&self) -> bool {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
match self {
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
Call::register_retirement_signal { .. } |
|
||||||
|
Call::revoke_retirement_signal { .. } |
|
||||||
|
Call::favor { .. } |
|
||||||
|
Call::revoke_favor { .. } |
|
||||||
|
Call::stand_against { .. } => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event from signals.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
/// A retirement signal has been registered.
|
||||||
RetirementSignalRegistered {
|
RetirementSignalRegistered {
|
||||||
signal_id: [u8; 32],
|
/// The retirement signal's ID.
|
||||||
|
signal: [u8; 32],
|
||||||
|
/// The protocol retirement is proposed in favor of.
|
||||||
in_favor_of: [u8; 32],
|
in_favor_of: [u8; 32],
|
||||||
|
/// The address which registered this signal.
|
||||||
registrant: SeraiAddress,
|
registrant: SeraiAddress,
|
||||||
},
|
},
|
||||||
|
/// A retirement signal was revoked.
|
||||||
RetirementSignalRevoked {
|
RetirementSignalRevoked {
|
||||||
signal_id: [u8; 32],
|
/// The retirement signal's ID.
|
||||||
|
signal: [u8; 32],
|
||||||
},
|
},
|
||||||
|
/// A signal was favored.
|
||||||
SignalFavored {
|
SignalFavored {
|
||||||
signal_id: SignalId,
|
/// The signal favored.
|
||||||
|
signal: Signal,
|
||||||
|
/// The validator the signal was favored by.
|
||||||
by: SeraiAddress,
|
by: SeraiAddress,
|
||||||
for_network: NetworkId,
|
/// The network with which the signal was favored.
|
||||||
},
|
with_network: NetworkId,
|
||||||
SetInFavor {
|
|
||||||
signal_id: SignalId,
|
|
||||||
set: ValidatorSet,
|
|
||||||
},
|
|
||||||
RetirementSignalLockedIn {
|
|
||||||
signal_id: [u8; 32],
|
|
||||||
},
|
|
||||||
SetNoLongerInFavor {
|
|
||||||
signal_id: SignalId,
|
|
||||||
set: ValidatorSet,
|
|
||||||
},
|
},
|
||||||
|
/// Favor for a signal was revoked.
|
||||||
FavorRevoked {
|
FavorRevoked {
|
||||||
signal_id: SignalId,
|
/// The signal whose favor was revoked.
|
||||||
|
signal: Signal,
|
||||||
|
/// The validator who revoked their favor for the signal.
|
||||||
by: SeraiAddress,
|
by: SeraiAddress,
|
||||||
for_network: NetworkId,
|
/// The network with which favor for the signal was revoked.
|
||||||
|
with_network: NetworkId,
|
||||||
},
|
},
|
||||||
|
/// A supermajority of a validator set now favor a signal.
|
||||||
|
SetInFavor {
|
||||||
|
/// The signal which now has a supermajority of a validator set favoring it.
|
||||||
|
signal: Signal,
|
||||||
|
/// The validator set which is now considered to favor the signal.
|
||||||
|
set: ValidatorSet,
|
||||||
|
},
|
||||||
|
/// A validator set is no longer considered to favor a signal.
|
||||||
|
SetNoLongerInFavor {
|
||||||
|
/// The signal which no longer has the validator set considered in favor of it.
|
||||||
|
signal: Signal,
|
||||||
|
/// The validator set which is no longer considered to be in favor of the signal.
|
||||||
|
set: ValidatorSet,
|
||||||
|
},
|
||||||
|
/// A retirement signal has been locked in.
|
||||||
|
RetirementSignalLockedIn {
|
||||||
|
/// The signal which has been locked in.
|
||||||
|
signal: [u8; 32],
|
||||||
|
},
|
||||||
|
/// A validator set's ability to publish batches was halted.
|
||||||
|
///
|
||||||
|
/// This also halts set rotation in effect, as handovers are via new sets starting to publish
|
||||||
|
/// batches.
|
||||||
|
SetHalted {
|
||||||
|
/// The signal which has been locked in.
|
||||||
|
signal: [u8; 32],
|
||||||
|
},
|
||||||
|
/// An account has stood against a signal.
|
||||||
AgainstSignal {
|
AgainstSignal {
|
||||||
signal_id: SignalId,
|
/// The signal stood against.
|
||||||
|
signal: Signal,
|
||||||
|
/// The account which stood against the signal.
|
||||||
who: SeraiAddress,
|
who: SeraiAddress,
|
||||||
for_network: NetworkId,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
use frame_support::dispatch::{DispatchInfo, DispatchError};
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use serai_primitives::SeraiAddress;
|
/// An event from the system.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
ExtrinsicSuccess { dispatch_info: DispatchInfo },
|
/// The transaction successfully executed.
|
||||||
ExtrinsicFailed { dispatch_error: DispatchError, dispatch_info: DispatchInfo },
|
TransactionSuccess,
|
||||||
CodeUpdated,
|
/// The transaction failed to execute.
|
||||||
NewAccount { account: SeraiAddress },
|
// TODO: Add an error to this
|
||||||
KilledAccount { account: SeraiAddress },
|
TransactionFailed,
|
||||||
Remarked { sender: SeraiAddress, hash: [u8; 32] },
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub enum Call {
|
|
||||||
set {
|
|
||||||
#[codec(compact)]
|
|
||||||
now: u64,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
563
substrate/abi/src/transaction.rs
Normal file
563
substrate/abi/src/transaction.rs
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
use core::num::NonZero;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use borsh::{io, BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
|
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||||
|
use serai_primitives::{BlockHash, address::SeraiAddress, balance::Amount, crypto::Signature};
|
||||||
|
use crate::Call;
|
||||||
|
|
||||||
|
/// The maximum amount of calls allowed in a transaction.
|
||||||
|
pub const MAX_CALLS: u32 = 8;
|
||||||
|
|
||||||
|
/// An error regarding `SignedCalls`.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum SignedCallsError {
|
||||||
|
/// No calls were included.
|
||||||
|
NoCalls,
|
||||||
|
/// Too many calls were included.
|
||||||
|
TooManyCalls,
|
||||||
|
/// An unsigned call was included.
|
||||||
|
IncludedUnsignedCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `Vec` of signed calls.
|
||||||
|
// We don't implement BorshDeserialize due to to maintained invariants on this struct.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize)]
|
||||||
|
pub struct SignedCalls(
|
||||||
|
#[borsh(serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bounded_vec")]
|
||||||
|
BoundedVec<Call, ConstU32<{ MAX_CALLS }>>,
|
||||||
|
);
|
||||||
|
impl TryFrom<Vec<Call>> for SignedCalls {
|
||||||
|
type Error = SignedCallsError;
|
||||||
|
fn try_from(calls: Vec<Call>) -> Result<Self, Self::Error> {
|
||||||
|
if calls.is_empty() {
|
||||||
|
Err(SignedCallsError::NoCalls)?;
|
||||||
|
}
|
||||||
|
for call in &calls {
|
||||||
|
if !call.is_signed() {
|
||||||
|
Err(SignedCallsError::IncludedUnsignedCall)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calls.try_into().map_err(|_| SignedCallsError::TooManyCalls).map(SignedCalls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error regarding `UnsignedCall`.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum UnsignedCallError {
|
||||||
|
/// A signed call was specified.
|
||||||
|
SignedCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An unsigned call.
|
||||||
|
// We don't implement BorshDeserialize due to to maintained invariants on this struct.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize)]
|
||||||
|
pub struct UnsignedCall(Call);
|
||||||
|
impl TryFrom<Call> for UnsignedCall {
|
||||||
|
type Error = UnsignedCallError;
|
||||||
|
fn try_from(call: Call) -> Result<Self, Self::Error> {
|
||||||
|
if call.is_signed() {
|
||||||
|
Err(UnsignedCallError::SignedCall)?;
|
||||||
|
}
|
||||||
|
Ok(UnsignedCall(call))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Part of the context used to sign with, from the protocol.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct ImplicitContext {
|
||||||
|
/// The genesis hash of the blockchain.
|
||||||
|
pub genesis: BlockHash,
|
||||||
|
/// The ID of the current protocol.
|
||||||
|
pub protocol_id: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Part of the context used to sign with, specified within the transaction itself.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct ExplicitContext {
|
||||||
|
/// The historic block this transaction builds upon.
|
||||||
|
///
|
||||||
|
/// This transaction can not be included in a blockchain which does not include this block.
|
||||||
|
pub historic_block: BlockHash,
|
||||||
|
|
||||||
|
/// The UNIX time this transaction must be included by (and expires after).
|
||||||
|
///
|
||||||
|
/// This transaction can not be included in a block whose time is equal or greater to this value.
|
||||||
|
pub include_by: Option<NonZero<u64>>,
|
||||||
|
|
||||||
|
/// The signer.
|
||||||
|
pub signer: SeraiAddress,
|
||||||
|
|
||||||
|
/// The signer's nonce.
|
||||||
|
pub nonce: u32,
|
||||||
|
|
||||||
|
/// The fee, in SRI, paid to the network for inclusion.
|
||||||
|
///
|
||||||
|
/// This fee is paid regardless of the success of any of the calls.
|
||||||
|
pub fee: Amount,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A signature, with context.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct ContextualizedSignature {
|
||||||
|
/// The explicit context.
|
||||||
|
explicit_context: ExplicitContext,
|
||||||
|
/// The signature.
|
||||||
|
signature: Signature,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Serai transaction.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum Transaction {
|
||||||
|
/// An unsigned transaction.
|
||||||
|
Unsigned {
|
||||||
|
/// The contained call.
|
||||||
|
call: UnsignedCall,
|
||||||
|
},
|
||||||
|
/// A signed transaction.
|
||||||
|
Signed {
|
||||||
|
/// The calls.
|
||||||
|
///
|
||||||
|
/// These calls are executed atomically. Either all successfully execute or none do. The
|
||||||
|
/// transaction's fee is paid regardless.
|
||||||
|
calls: SignedCalls,
|
||||||
|
/// The signature for this transaction.
|
||||||
|
///
|
||||||
|
/// This is not checked on deserializtion and may be invalid.
|
||||||
|
contextualized_signature: ContextualizedSignature,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BorshSerialize for Transaction {
|
||||||
|
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
Transaction::Unsigned { call } => {
|
||||||
|
/*
|
||||||
|
`Signed` `Transaction`s encode the length of their `Vec<Call>` here. Since that `Vec` is
|
||||||
|
bound to be non-empty, it will never write `0`, enabling `Unsigned` to use it.
|
||||||
|
|
||||||
|
The benefit to these not overlapping is in the ability to determine if the `Transaction`
|
||||||
|
has a signature or not. If this wrote a `1`, for the amount of `Call`s present in the
|
||||||
|
`Transaction`, that `Call` would have to be introspected for if its signed or not. With
|
||||||
|
the usage of `0`, given how low `MAX_CALLS` is, this `Transaction` can technically be
|
||||||
|
defined as an enum of
|
||||||
|
`0 Call, 1 Call ContextualizedSignature, 2 Call Call ContextualizedSignature ...`, to
|
||||||
|
maintain compatbility with the borsh specification without wrapper functions. The checks
|
||||||
|
here on `Call` types/quantity could be moved to later validation functions.
|
||||||
|
*/
|
||||||
|
writer.write_all(&[0])?;
|
||||||
|
call.serialize(writer)
|
||||||
|
}
|
||||||
|
Transaction::Signed { calls, contextualized_signature } => {
|
||||||
|
serai_primitives::sp_borsh::borsh_serialize_bounded_vec(&calls.0, writer)?;
|
||||||
|
contextualized_signature.serialize(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BorshDeserialize for Transaction {
|
||||||
|
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||||
|
let mut len = [0xff];
|
||||||
|
reader.read_exact(&mut len)?;
|
||||||
|
let len = len[0];
|
||||||
|
|
||||||
|
if len == 0 {
|
||||||
|
let call = Call::deserialize_reader(reader)?;
|
||||||
|
if call.is_signed() {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, "call was signed but marked unsigned"))?;
|
||||||
|
}
|
||||||
|
Ok(Transaction::Unsigned { call: UnsignedCall(call) })
|
||||||
|
} else {
|
||||||
|
if u32::from(len) > MAX_CALLS {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, "too many calls"))?;
|
||||||
|
}
|
||||||
|
let mut calls = BoundedVec::with_bounded_capacity(len.into());
|
||||||
|
for _ in 0 .. len {
|
||||||
|
let call = Call::deserialize_reader(reader)?;
|
||||||
|
if !call.is_signed() {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, "call was unsigned but included as signed"))?;
|
||||||
|
}
|
||||||
|
calls.try_push(call).unwrap();
|
||||||
|
}
|
||||||
|
let contextualized_signature = ContextualizedSignature::deserialize_reader(reader)?;
|
||||||
|
Ok(Transaction::Signed { calls: SignedCalls(calls), contextualized_signature })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transaction {
|
||||||
|
/// The message to sign to produce a signature.
|
||||||
|
pub fn signature_message(
|
||||||
|
calls: &SignedCalls,
|
||||||
|
implicit_context: &ImplicitContext,
|
||||||
|
explicit_context: &ExplicitContext,
|
||||||
|
) -> Vec<u8> {
|
||||||
|
let mut message = Vec::with_capacity(
|
||||||
|
(calls.0.len() * 64) +
|
||||||
|
core::mem::size_of::<ImplicitContext>() +
|
||||||
|
core::mem::size_of::<ExplicitContext>(),
|
||||||
|
);
|
||||||
|
calls.serialize(&mut message).unwrap();
|
||||||
|
implicit_context.serialize(&mut message).unwrap();
|
||||||
|
explicit_context.serialize(&mut message).unwrap();
|
||||||
|
message
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The unique hash of this transaction.
|
||||||
|
///
|
||||||
|
/// No two transactions on the blockchain will share a hash, making this a unique identifier.
|
||||||
|
/// For signed transactions, this is due to the `(signer, nonce)` pair present within the
|
||||||
|
/// `ExplicitContext`. For unsigned transactions, this is due to inherent properties of their
|
||||||
|
/// execution (e.g. only being able to set a `ValidatorSet`'s keys once).
|
||||||
|
pub fn hash(&self) -> [u8; 32] {
|
||||||
|
sp_core::blake2_256(&match self {
|
||||||
|
Transaction::Unsigned { call } => borsh::to_vec(&call).unwrap(),
|
||||||
|
Transaction::Signed {
|
||||||
|
calls,
|
||||||
|
contextualized_signature: ContextualizedSignature { explicit_context, signature: _ },
|
||||||
|
} => {
|
||||||
|
// We explicitly don't hash the signature, so signatures can be replaced in the future if
|
||||||
|
// desired (such as with half-aggregated Schnorr signatures)
|
||||||
|
borsh::to_vec(&(calls, explicit_context)).unwrap()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "substrate")]
|
||||||
|
mod substrate {
|
||||||
|
use core::fmt::Debug;
|
||||||
|
use alloc::vec;
|
||||||
|
|
||||||
|
use scale::{Encode, Decode};
|
||||||
|
use sp_runtime::{
|
||||||
|
transaction_validity::*,
|
||||||
|
traits::{Verify, ExtrinsicLike, Dispatchable, ValidateUnsigned, Checkable, Applyable},
|
||||||
|
Weight,
|
||||||
|
};
|
||||||
|
#[rustfmt::skip]
|
||||||
|
use frame_support::dispatch::{DispatchClass, Pays, DispatchInfo, GetDispatchInfo, PostDispatchInfo};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl Encode for Transaction {
|
||||||
|
fn encode(&self) -> Vec<u8> {
|
||||||
|
borsh::to_vec(self).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Decode for Transaction {
|
||||||
|
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||||
|
struct ScaleRead<'a, I: scale::Input>(&'a mut I, Option<scale::Error>);
|
||||||
|
impl<I: scale::Input> borsh::io::Read for ScaleRead<'_, I> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result<usize> {
|
||||||
|
let remaining_len = self.0.remaining_len().map_err(|err| {
|
||||||
|
self.1 = Some(err);
|
||||||
|
borsh::io::Error::new(borsh::io::ErrorKind::Other, "")
|
||||||
|
})?;
|
||||||
|
// If we're still calling `read`, we try to read at least one more byte
|
||||||
|
let to_read = buf.len().min(remaining_len.unwrap_or(1));
|
||||||
|
self.0.read(&mut buf[.. to_read]).map_err(|err| {
|
||||||
|
self.1 = Some(err);
|
||||||
|
borsh::io::Error::new(borsh::io::ErrorKind::Other, "")
|
||||||
|
})?;
|
||||||
|
Ok(to_read)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut input = ScaleRead(input, None);
|
||||||
|
match Self::deserialize_reader(&mut input) {
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
Err(_) => Err(input.1.unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The context which transactions are executed in.
|
||||||
|
pub trait TransactionContext: 'static + Send + Sync + Clone + PartialEq + Eq + Debug {
|
||||||
|
/// The base weight for a signed transaction.
|
||||||
|
const SIGNED_WEIGHT: Weight;
|
||||||
|
|
||||||
|
/// The call type for the runtime.
|
||||||
|
type RuntimeCall: From<Call>
|
||||||
|
+ GetDispatchInfo
|
||||||
|
+ Dispatchable<
|
||||||
|
RuntimeOrigin: From<Option<SeraiAddress>>,
|
||||||
|
Info = DispatchInfo,
|
||||||
|
PostInfo = PostDispatchInfo,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// The implicit context to verify transactions with.
|
||||||
|
fn implicit_context() -> ImplicitContext;
|
||||||
|
|
||||||
|
/// If a block is present in the blockchain.
|
||||||
|
fn block_is_present_in_blockchain(&self, hash: &BlockHash) -> bool;
|
||||||
|
/// The time embedded into the current block.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the time has yet to be set.
|
||||||
|
fn current_time(&self) -> Option<u64>;
|
||||||
|
/// Get the next nonce for an account.
|
||||||
|
fn next_nonce(&self, signer: &SeraiAddress) -> u32;
|
||||||
|
/// If the signer can pay the SRI fee.
|
||||||
|
fn can_pay_fee(
|
||||||
|
&self,
|
||||||
|
signer: &SeraiAddress,
|
||||||
|
fee: Amount,
|
||||||
|
) -> Result<(), TransactionValidityError>;
|
||||||
|
|
||||||
|
/// Begin execution of a transaction.
|
||||||
|
fn start_transaction(&self);
|
||||||
|
/// Consume the next nonce for an account.
|
||||||
|
fn consume_next_nonce(&self, signer: &SeraiAddress);
|
||||||
|
/// Have the transaction pay its SRI fee.
|
||||||
|
fn pay_fee(&self, signer: &SeraiAddress, fee: Amount) -> Result<(), TransactionValidityError>;
|
||||||
|
/// End execution of a transaction.
|
||||||
|
fn end_transaction(&self, transaction_hash: [u8; 32]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A transaction with the context necessary to evaluate it within Substrate.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
|
pub struct TransactionWithContext<Context: TransactionContext>(
|
||||||
|
Transaction,
|
||||||
|
#[codec(skip)] Context,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl ExtrinsicLike for Transaction {
|
||||||
|
fn is_signed(&self) -> Option<bool> {
|
||||||
|
Some(matches!(self, Transaction::Signed { .. }))
|
||||||
|
}
|
||||||
|
fn is_bare(&self) -> bool {
|
||||||
|
matches!(self, Transaction::Unsigned { .. })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Context: TransactionContext> GetDispatchInfo for TransactionWithContext<Context> {
|
||||||
|
fn get_dispatch_info(&self) -> DispatchInfo {
|
||||||
|
match &self.0 {
|
||||||
|
Transaction::Unsigned { call } => DispatchInfo {
|
||||||
|
call_weight: Context::RuntimeCall::from(call.0.clone()).get_dispatch_info().call_weight,
|
||||||
|
extension_weight: Weight::zero(),
|
||||||
|
class: DispatchClass::Operational,
|
||||||
|
pays_fee: Pays::No,
|
||||||
|
},
|
||||||
|
Transaction::Signed { calls, .. } => DispatchInfo {
|
||||||
|
call_weight: calls
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|call| Context::RuntimeCall::from(call).get_dispatch_info().call_weight)
|
||||||
|
.fold(Weight::zero(), |accum, item| accum + item),
|
||||||
|
extension_weight: Context::SIGNED_WEIGHT,
|
||||||
|
class: DispatchClass::Normal,
|
||||||
|
pays_fee: Pays::Yes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Context: TransactionContext> Checkable<Context> for Transaction {
|
||||||
|
type Checked = TransactionWithContext<Context>;
|
||||||
|
|
||||||
|
fn check(self, context: &Context) -> Result<Self::Checked, TransactionValidityError> {
|
||||||
|
match &self {
|
||||||
|
Transaction::Unsigned { .. } => {}
|
||||||
|
Transaction::Signed {
|
||||||
|
calls,
|
||||||
|
contextualized_signature: ContextualizedSignature { explicit_context, signature },
|
||||||
|
} => {
|
||||||
|
if !sp_core::sr25519::Signature::from(*signature).verify(
|
||||||
|
Transaction::signature_message(calls, &Context::implicit_context(), explicit_context)
|
||||||
|
.as_slice(),
|
||||||
|
&sp_core::sr25519::Public::from(explicit_context.signer),
|
||||||
|
) {
|
||||||
|
Err(InvalidTransaction::BadProof)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(TransactionWithContext(self, context.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn unchecked_into_checked_i_know_what_i_am_doing(
|
||||||
|
self,
|
||||||
|
c: &Context,
|
||||||
|
) -> Result<Self::Checked, TransactionValidityError> {
|
||||||
|
// This satisfies the API, not necessarily the intent, yet this fn is only intended to be used
|
||||||
|
// within tests. Accordingly, it's fine to be stricter than necessarily
|
||||||
|
self.check(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Context: TransactionContext> TransactionWithContext<Context> {
|
||||||
|
fn validate_except_fee<V: ValidateUnsigned<Call = Context::RuntimeCall>>(
|
||||||
|
&self,
|
||||||
|
source: TransactionSource,
|
||||||
|
mempool_priority_if_signed: u64,
|
||||||
|
) -> TransactionValidity {
|
||||||
|
match &self.0 {
|
||||||
|
Transaction::Unsigned { call } => {
|
||||||
|
let ValidTransaction { priority: _, requires, provides, longevity: _, propagate: _ } =
|
||||||
|
V::validate_unsigned(source, &Context::RuntimeCall::from(call.0.clone()))?;
|
||||||
|
Ok(ValidTransaction {
|
||||||
|
// We should always try to include unsigned transactions prior to signed
|
||||||
|
priority: u64::MAX,
|
||||||
|
requires,
|
||||||
|
provides,
|
||||||
|
// This is valid until included
|
||||||
|
longevity: u64::MAX,
|
||||||
|
// Ensure this is propagated
|
||||||
|
propagate: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Transaction::Signed { calls: _, contextualized_signature } => {
|
||||||
|
let ExplicitContext { historic_block, include_by, signer, nonce, fee: _ } =
|
||||||
|
&contextualized_signature.explicit_context;
|
||||||
|
if !self.1.block_is_present_in_blockchain(historic_block) {
|
||||||
|
// We don't know if this is a block from a fundamentally distinct blockchain or a
|
||||||
|
// continuation of this blockchain we have yet to sync (which would be `Future`)
|
||||||
|
Err(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup))?;
|
||||||
|
}
|
||||||
|
if let Some(include_by) = *include_by {
|
||||||
|
if let Some(current_time) = self.1.current_time() {
|
||||||
|
if current_time >= u64::from(include_by) {
|
||||||
|
// Since this transaction has a time bound which has passed, error
|
||||||
|
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Since this transaction has a time bound, yet we don't know the time, error
|
||||||
|
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self.1.next_nonce(signer).cmp(nonce) {
|
||||||
|
core::cmp::Ordering::Less => {
|
||||||
|
Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))?
|
||||||
|
}
|
||||||
|
core::cmp::Ordering::Equal => {}
|
||||||
|
core::cmp::Ordering::Greater => {
|
||||||
|
Err(TransactionValidityError::Invalid(InvalidTransaction::Future))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let requires = if let Some(prior_nonce) = nonce.checked_sub(1) {
|
||||||
|
vec![borsh::to_vec(&(signer, prior_nonce)).unwrap()]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
let provides = vec![borsh::to_vec(&(signer, nonce)).unwrap()];
|
||||||
|
Ok(ValidTransaction {
|
||||||
|
priority: mempool_priority_if_signed,
|
||||||
|
requires,
|
||||||
|
provides,
|
||||||
|
// This revalidates the transaction every block. This is required due to this being
|
||||||
|
// denominated in blocks, and our transaction expiration being denominated in seconds.
|
||||||
|
longevity: 1,
|
||||||
|
propagate: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Context: TransactionContext> Applyable for TransactionWithContext<Context> {
|
||||||
|
type Call = Context::RuntimeCall;
|
||||||
|
|
||||||
|
fn validate<V: ValidateUnsigned<Call = Context::RuntimeCall>>(
|
||||||
|
&self,
|
||||||
|
source: TransactionSource,
|
||||||
|
info: &DispatchInfo,
|
||||||
|
_len: usize,
|
||||||
|
) -> TransactionValidity {
|
||||||
|
let mempool_priority_if_signed = match &self.0 {
|
||||||
|
Transaction::Unsigned { .. } => {
|
||||||
|
// Since this is the priority if signed, and this isn't signed, we return 0
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Transaction::Signed {
|
||||||
|
calls: _,
|
||||||
|
contextualized_signature:
|
||||||
|
ContextualizedSignature { explicit_context: ExplicitContext { signer, fee, .. }, .. },
|
||||||
|
} => {
|
||||||
|
self.1.can_pay_fee(signer, *fee)?;
|
||||||
|
|
||||||
|
// Prioritize transactions by their fees
|
||||||
|
// TODO: Re-evaluate this
|
||||||
|
{
|
||||||
|
let fee = fee.0;
|
||||||
|
Weight::from_all(fee).checked_div_per_component(&info.call_weight).unwrap_or(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.validate_except_fee::<V>(source, mempool_priority_if_signed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply<V: ValidateUnsigned<Call = Context::RuntimeCall>>(
|
||||||
|
self,
|
||||||
|
_info: &DispatchInfo,
|
||||||
|
_len: usize,
|
||||||
|
) -> sp_runtime::ApplyExtrinsicResultWithInfo<PostDispatchInfo> {
|
||||||
|
// We use 0 for the mempool priority, as this is no longer in the mempool so it's irrelevant
|
||||||
|
self.validate_except_fee::<V>(TransactionSource::InBlock, 0)?;
|
||||||
|
|
||||||
|
// Start the transaction
|
||||||
|
self.1.start_transaction();
|
||||||
|
|
||||||
|
let transaction_hash = self.0.hash();
|
||||||
|
|
||||||
|
let res = match self.0 {
|
||||||
|
Transaction::Unsigned { call } => {
|
||||||
|
let call = Context::RuntimeCall::from(call.0);
|
||||||
|
V::pre_dispatch(&call)?;
|
||||||
|
match call.dispatch(None.into()) {
|
||||||
|
Ok(res) => Ok(Ok(res)),
|
||||||
|
// Unsigned transactions should only be included if valid in all regards
|
||||||
|
Err(_err) => Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(0))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Transaction::Signed {
|
||||||
|
calls,
|
||||||
|
contextualized_signature:
|
||||||
|
ContextualizedSignature { explicit_context: ExplicitContext { signer, fee, .. }, .. },
|
||||||
|
} => {
|
||||||
|
// Consume the signer's next nonce
|
||||||
|
self.1.consume_next_nonce(&signer);
|
||||||
|
// Pay the fee
|
||||||
|
self.1.pay_fee(&signer, fee)?;
|
||||||
|
|
||||||
|
let _res = frame_support::storage::transactional::with_storage_layer(|| {
|
||||||
|
for call in calls.0 {
|
||||||
|
let call = Context::RuntimeCall::from(call);
|
||||||
|
match call.dispatch(Some(signer).into()) {
|
||||||
|
Ok(_res) => {}
|
||||||
|
// Because this call errored, don't continue and revert all prior calls
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
// We don't care if the individual calls succeeded or failed.
|
||||||
|
// The transaction was valid for inclusion and the fee was paid.
|
||||||
|
// Either the calls passed, as desired, or they failed and the storage was reverted.
|
||||||
|
Ok(Ok(PostDispatchInfo {
|
||||||
|
// `None` stands for the worst case, which is what we want
|
||||||
|
actual_weight: None,
|
||||||
|
// Signed transactions always pay their fee
|
||||||
|
// TODO: Do we want to handle this so we can not charge fees on removing genesis
|
||||||
|
// liquidity?
|
||||||
|
pays_fee: Pays::Yes,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: TransactionSuccess/TransactionFailure event?
|
||||||
|
|
||||||
|
// End the transaction
|
||||||
|
self.1.end_transaction(transaction_hash);
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "substrate")]
|
||||||
|
pub use substrate::*;
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
use scale::Encode;
|
|
||||||
|
|
||||||
use sp_core::sr25519::{Public, Signature};
|
|
||||||
use sp_runtime::traits::Verify;
|
|
||||||
|
|
||||||
use serai_primitives::SeraiAddress;
|
|
||||||
|
|
||||||
use frame_support::dispatch::GetDispatchInfo;
|
|
||||||
|
|
||||||
pub trait TransactionMember:
|
|
||||||
Clone + PartialEq + Eq + core::fmt::Debug + scale::Encode + scale::Decode + scale_info::TypeInfo
|
|
||||||
{
|
|
||||||
}
|
|
||||||
impl<
|
|
||||||
T: Clone
|
|
||||||
+ PartialEq
|
|
||||||
+ Eq
|
|
||||||
+ core::fmt::Debug
|
|
||||||
+ scale::Encode
|
|
||||||
+ scale::Decode
|
|
||||||
+ scale_info::TypeInfo,
|
|
||||||
> TransactionMember for T
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
type TransactionEncodeAs<'a, Extra> =
|
|
||||||
(&'a crate::Call, &'a Option<(SeraiAddress, Signature, Extra)>);
|
|
||||||
type TransactionDecodeAs<Extra> = (crate::Call, Option<(SeraiAddress, Signature, Extra)>);
|
|
||||||
|
|
||||||
// We use our own Transaction struct, over UncheckedExtrinsic, for more control, a bit more
|
|
||||||
// simplicity, and in order to be immune to https://github.com/paritytech/polkadot-sdk/issues/2947
|
|
||||||
#[allow(private_bounds)]
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub struct Transaction<
|
|
||||||
Call: 'static + TransactionMember + From<crate::Call>,
|
|
||||||
Extra: 'static + TransactionMember,
|
|
||||||
> {
|
|
||||||
call: crate::Call,
|
|
||||||
mapped_call: Call,
|
|
||||||
signature: Option<(SeraiAddress, Signature, Extra)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
|
||||||
Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
pub fn new(call: crate::Call, signature: Option<(SeraiAddress, Signature, Extra)>) -> Self {
|
|
||||||
Self { call: call.clone(), mapped_call: call.into(), signature }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(&self) -> &crate::Call {
|
|
||||||
&self.call
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
|
||||||
scale::Encode for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
|
||||||
let tx: TransactionEncodeAs<Extra> = (&self.call, &self.signature);
|
|
||||||
tx.using_encoded(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
|
||||||
scale::Decode for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
|
||||||
let (call, signature) = TransactionDecodeAs::decode(input)?;
|
|
||||||
let mapped_call = Call::from(call.clone());
|
|
||||||
Ok(Self { call, mapped_call, signature })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
|
||||||
scale_info::TypeInfo for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
type Identity = TransactionDecodeAs<Extra>;
|
|
||||||
|
|
||||||
// Define the type info as the info of the type equivalent to what we encode as
|
|
||||||
fn type_info() -> scale_info::Type {
|
|
||||||
TransactionDecodeAs::<Extra>::type_info()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
mod _serde {
|
|
||||||
use scale::Encode;
|
|
||||||
use serde::ser::*;
|
|
||||||
use super::*;
|
|
||||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
|
||||||
Serialize for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
let encoded = self.encode();
|
|
||||||
serializer.serialize_bytes(&encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use serde::de::*;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl<
|
|
||||||
'a,
|
|
||||||
Call: 'static + TransactionMember + From<crate::Call>,
|
|
||||||
Extra: 'static + TransactionMember,
|
|
||||||
> Deserialize<'a> for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
|
||||||
let bytes = sp_core::bytes::deserialize(de)?;
|
|
||||||
<Self as scale::Decode>::decode(&mut &bytes[..])
|
|
||||||
.map_err(|e| serde::de::Error::custom(format!("invalid transaction: {e}")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
|
||||||
Extra: 'static + TransactionMember,
|
|
||||||
> sp_runtime::traits::Extrinsic for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
type Call = Call;
|
|
||||||
type SignaturePayload = (SeraiAddress, Signature, Extra);
|
|
||||||
fn is_signed(&self) -> Option<bool> {
|
|
||||||
Some(self.signature.is_some())
|
|
||||||
}
|
|
||||||
fn new(call: Call, signature: Option<Self::SignaturePayload>) -> Option<Self> {
|
|
||||||
Some(Self { call: call.clone().try_into().ok()?, mapped_call: call, signature })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
|
||||||
Extra: 'static + TransactionMember,
|
|
||||||
> frame_support::traits::ExtrinsicCall for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
fn call(&self) -> &Call {
|
|
||||||
&self.mapped_call
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
Call: 'static + TransactionMember + From<crate::Call>,
|
|
||||||
Extra: 'static + TransactionMember + sp_runtime::traits::SignedExtension,
|
|
||||||
> sp_runtime::traits::ExtrinsicMetadata for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
type SignedExtensions = Extra;
|
|
||||||
|
|
||||||
const VERSION: u8 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
Call: 'static + TransactionMember + From<crate::Call> + GetDispatchInfo,
|
|
||||||
Extra: 'static + TransactionMember,
|
|
||||||
> GetDispatchInfo for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
fn get_dispatch_info(&self) -> frame_support::dispatch::DispatchInfo {
|
|
||||||
self.mapped_call.get_dispatch_info()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
Call: 'static + TransactionMember + From<crate::Call>,
|
|
||||||
Extra: 'static + TransactionMember + sp_runtime::traits::SignedExtension,
|
|
||||||
> sp_runtime::traits::BlindCheckable for Transaction<Call, Extra>
|
|
||||||
{
|
|
||||||
type Checked = sp_runtime::generic::CheckedExtrinsic<Public, Call, Extra>;
|
|
||||||
|
|
||||||
fn check(
|
|
||||||
self,
|
|
||||||
) -> Result<Self::Checked, sp_runtime::transaction_validity::TransactionValidityError> {
|
|
||||||
Ok(match self.signature {
|
|
||||||
Some((signer, signature, extra)) => {
|
|
||||||
if !signature.verify(
|
|
||||||
(&self.call, &extra, extra.additional_signed()?).encode().as_slice(),
|
|
||||||
&signer.into(),
|
|
||||||
) {
|
|
||||||
Err(sp_runtime::transaction_validity::InvalidTransaction::BadProof)?
|
|
||||||
}
|
|
||||||
|
|
||||||
sp_runtime::generic::CheckedExtrinsic {
|
|
||||||
signed: Some((signer.into(), extra)),
|
|
||||||
function: self.mapped_call,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => sp_runtime::generic::CheckedExtrinsic { signed: None, function: self.mapped_call },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +1,145 @@
|
|||||||
|
use borsh::{BorshSerialize, BorshDeserialize};
|
||||||
|
|
||||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||||
|
|
||||||
pub use serai_validator_sets_primitives as primitives;
|
use serai_primitives::{
|
||||||
|
crypto::{ExternalKey, KeyPair, Signature},
|
||||||
|
address::SeraiAddress,
|
||||||
|
balance::Amount,
|
||||||
|
network_id::*,
|
||||||
|
validator_sets::*,
|
||||||
|
};
|
||||||
|
|
||||||
use serai_primitives::*;
|
/// A call to the validator sets.
|
||||||
use serai_validator_sets_primitives::*;
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
|
||||||
pub enum Call {
|
pub enum Call {
|
||||||
|
/// Set the keys for a validator set.
|
||||||
set_keys {
|
set_keys {
|
||||||
network: ExternalNetworkId,
|
/// The validator set which is setting their keys.
|
||||||
|
validator_set: ExternalValidatorSet,
|
||||||
|
/// The keys being set.
|
||||||
key_pair: KeyPair,
|
key_pair: KeyPair,
|
||||||
|
/// The participants in the validator set who signed off on these keys.
|
||||||
|
// TODO: Bound
|
||||||
|
#[borsh(
|
||||||
|
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bitvec",
|
||||||
|
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bitvec"
|
||||||
|
)]
|
||||||
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||||
|
/// The signature confirming these keys are valid.
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
},
|
},
|
||||||
set_embedded_elliptic_curve_key {
|
/// Report a validator set's slashes onto Serai.
|
||||||
embedded_elliptic_curve: EmbeddedEllipticCurve,
|
|
||||||
key: BoundedVec<u8, ConstU32<{ MAX_KEY_LEN }>>,
|
|
||||||
},
|
|
||||||
report_slashes {
|
report_slashes {
|
||||||
network: ExternalNetworkId,
|
/// The validator set which is setting their keys.
|
||||||
|
validator_set: ExternalValidatorSet,
|
||||||
|
/// The slashes they're reporting.
|
||||||
slashes: SlashReport,
|
slashes: SlashReport,
|
||||||
|
/// The signature confirming the validity of this slash report.
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
},
|
},
|
||||||
|
/// Set a validator's keys on embedded elliptic curves for a specific network.
|
||||||
|
set_embedded_elliptic_curve_keys {
|
||||||
|
/// The network the origin is setting their embedded elliptic curve keys for.
|
||||||
|
network: ExternalNetworkId,
|
||||||
|
/// The keys on the embedded elliptic curves.
|
||||||
|
///
|
||||||
|
/// This may be a single key if the external network uses the same embedded elliptic curve as
|
||||||
|
/// used for the key to oraclize onto Serai.
|
||||||
|
#[borsh(
|
||||||
|
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bounded_vec",
|
||||||
|
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bounded_vec"
|
||||||
|
)]
|
||||||
|
keys: BoundedVec<u8, ConstU32<{ 2 * ExternalKey::MAX_LEN }>>,
|
||||||
|
},
|
||||||
|
/// Allocate stake to a network.
|
||||||
allocate {
|
allocate {
|
||||||
|
/// The network to allocate stake to.
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
|
/// The amount of stake to allocate.
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
},
|
},
|
||||||
|
/// Deallocate stake from a network.
|
||||||
|
///
|
||||||
|
/// This deallocation may be immediate or may be delayed depending on if the origin is an
|
||||||
|
/// active, or even recent, validator. If delayed, it will have to be claimed at a later time.
|
||||||
deallocate {
|
deallocate {
|
||||||
|
/// The network to deallocate stake from.
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
|
/// The amount of stake to deallocate.
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
},
|
},
|
||||||
|
/// Claim a now-unlocked deallocation.
|
||||||
claim_deallocation {
|
claim_deallocation {
|
||||||
network: NetworkId,
|
/// The validator set which claiming the deallocation was delayed until.
|
||||||
session: Session,
|
deallocation: ValidatorSet,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
impl Call {
|
||||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
pub(crate) fn is_signed(&self) -> bool {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
match self {
|
||||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
Call::set_keys { .. } | Call::report_slashes { .. } => false,
|
||||||
|
Call::set_embedded_elliptic_curve_keys { .. } |
|
||||||
|
Call::allocate { .. } |
|
||||||
|
Call::deallocate { .. } |
|
||||||
|
Call::claim_deallocation { .. } => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event from the validator sets.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
/// A new validator set was declared.
|
||||||
NewSet {
|
NewSet {
|
||||||
|
/// The set declared.
|
||||||
set: ValidatorSet,
|
set: ValidatorSet,
|
||||||
},
|
},
|
||||||
ParticipantRemoved {
|
/// A validator set has set their keys.
|
||||||
set: ValidatorSet,
|
SetKeys {
|
||||||
removed: SeraiAddress,
|
/// The set which set their keys.
|
||||||
},
|
|
||||||
KeyGen {
|
|
||||||
set: ExternalValidatorSet,
|
set: ExternalValidatorSet,
|
||||||
|
/// The keys sets.
|
||||||
key_pair: KeyPair,
|
key_pair: KeyPair,
|
||||||
},
|
},
|
||||||
|
/// A validator set has accepted responsibility from the prior validator set.
|
||||||
AcceptedHandover {
|
AcceptedHandover {
|
||||||
|
/// The set which accepted responsibility from the prior set.
|
||||||
set: ValidatorSet,
|
set: ValidatorSet,
|
||||||
},
|
},
|
||||||
|
/// A validator set has retired.
|
||||||
SetRetired {
|
SetRetired {
|
||||||
|
/// The set retired.
|
||||||
set: ValidatorSet,
|
set: ValidatorSet,
|
||||||
},
|
},
|
||||||
AllocationIncreased {
|
/// A validator's allocation to a network has increased.
|
||||||
|
Allocation {
|
||||||
|
/// The validator who increased their allocation.
|
||||||
validator: SeraiAddress,
|
validator: SeraiAddress,
|
||||||
|
/// The network the stake was allocated to.
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
|
/// The amount of stake allocated.
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
},
|
},
|
||||||
AllocationDecreased {
|
/// A validator's allocation to a network has decreased.
|
||||||
|
Deallocation {
|
||||||
|
/// The validator who decreased their allocation.
|
||||||
validator: SeraiAddress,
|
validator: SeraiAddress,
|
||||||
|
/// The network the stake was deallocated from.
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
|
/// The amount of stake deallocated.
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
|
/// The session which claiming the deallocation was delayed until.
|
||||||
delayed_until: Option<Session>,
|
delayed_until: Option<Session>,
|
||||||
},
|
},
|
||||||
|
/// A validator's deallocation from a network has been claimed.
|
||||||
|
///
|
||||||
|
/// This is only emited for deallocations which were delayed and has to be explicitly claimed.
|
||||||
DeallocationClaimed {
|
DeallocationClaimed {
|
||||||
|
/// The validator who claimed their deallocation.
|
||||||
validator: SeraiAddress,
|
validator: SeraiAddress,
|
||||||
network: NetworkId,
|
/// The validator set the deallocation was delayed until.
|
||||||
session: Session,
|
deallocation: ValidatorSet,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ serde_json = { version = "1", optional = true }
|
|||||||
serai-abi = { path = "../abi", version = "0.1" }
|
serai-abi = { path = "../abi", version = "0.1" }
|
||||||
|
|
||||||
multiaddr = { version = "0.18", optional = true }
|
multiaddr = { version = "0.18", optional = true }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", optional = true }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", optional = true }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", optional = true }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", optional = true }
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", optional = true }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", optional = true }
|
||||||
|
|
||||||
async-lock = "3"
|
async-lock = "3"
|
||||||
|
|
||||||
@@ -60,8 +60,7 @@ dockertest = "0.5"
|
|||||||
serai-docker-tests = { path = "../../tests/docker" }
|
serai-docker-tests = { path = "../../tests/docker" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
serai = ["thiserror/std", "serde", "serde_json", "serai-abi/serde", "multiaddr", "sp-core", "sp-runtime", "frame-system", "simple-request"]
|
serai = ["thiserror/std", "serde", "serde_json", "multiaddr", "sp-core", "sp-runtime", "frame-system", "simple-request"]
|
||||||
borsh = ["serai-abi/borsh"]
|
|
||||||
|
|
||||||
networks = []
|
networks = []
|
||||||
bitcoin = ["networks", "dep:bitcoin"]
|
bitcoin = ["networks", "dep:bitcoin"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use sp_core::bounded_vec::BoundedVec;
|
use sp_core::bounded::BoundedVec;
|
||||||
use serai_abi::primitives::{Amount, Coin, ExternalCoin, SeraiAddress};
|
use serai_abi::primitives::{Amount, Coin, ExternalCoin, SeraiAddress};
|
||||||
|
|
||||||
use crate::{SeraiError, TemporalSerai};
|
use crate::{SeraiError, TemporalSerai};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use rand_core::OsRng;
|
|||||||
|
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
ConstU32,
|
ConstU32,
|
||||||
bounded_vec::BoundedVec,
|
bounded::BoundedVec,
|
||||||
sr25519::{Pair, Signature},
|
sr25519::{Pair, Signature},
|
||||||
Pair as PairTrait,
|
Pair as PairTrait,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use rand_core::{RngCore, OsRng};
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
use sp_core::{Pair as PairTrait, bounded_vec::BoundedVec};
|
use sp_core::{Pair as PairTrait, bounded::BoundedVec};
|
||||||
|
|
||||||
use serai_abi::in_instructions::primitives::DexCall;
|
use serai_abi::in_instructions::primitives::DexCall;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name = "serai-coins-pallet"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Coins pallet for Serai"
|
description = "Coins pallet for Serai"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/coins/pallet"
|
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/coins"
|
||||||
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
@@ -22,41 +22,33 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
serai-primitives = { path = "../primitives", default-features = false, features = ["serde", "non_canonical_scale_derivations"] }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false, features = ["serde"] }
|
|
||||||
coins-primitives = { package = "serai-coins-primitives", path = "../primitives", default-features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false, features = ["std"] }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
"frame-system/std",
|
|
||||||
"frame-support/std",
|
|
||||||
|
|
||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
"sp-runtime/std",
|
"sp-runtime/std",
|
||||||
|
|
||||||
"pallet-transaction-payment/std",
|
"frame-system/std",
|
||||||
|
"frame-support/std",
|
||||||
|
|
||||||
"serai-primitives/std",
|
"serai-primitives/std",
|
||||||
"coins-primitives/std",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
try-runtime = [
|
try-runtime = [
|
||||||
"frame-system/try-runtime",
|
"frame-system/try-runtime",
|
||||||
"frame-support/try-runtime",
|
"frame-support/try-runtime",
|
||||||
|
|
||||||
"sp-runtime/try-runtime",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
3
substrate/coins/README.md
Normal file
3
substrate/coins/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Coins Pallet
|
||||||
|
|
||||||
|
Pallet implementing the necessary coins logic for the Serai protocol.
|
||||||
@@ -1,303 +0,0 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod mock;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use serai_primitives::{Balance, Coin, ExternalBalance, SubstrateAmount};
|
|
||||||
|
|
||||||
pub trait AllowMint {
|
|
||||||
fn is_allowed(balance: &ExternalBalance) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AllowMint for () {
|
|
||||||
fn is_allowed(_: &ExternalBalance) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Investigate why Substrate generates this
|
|
||||||
#[allow(unreachable_patterns, clippy::cast_possible_truncation)]
|
|
||||||
#[frame_support::pallet]
|
|
||||||
pub mod pallet {
|
|
||||||
use super::*;
|
|
||||||
use sp_std::{vec::Vec, any::TypeId};
|
|
||||||
use sp_core::sr25519::Public;
|
|
||||||
use sp_runtime::{
|
|
||||||
traits::{DispatchInfoOf, PostDispatchInfoOf},
|
|
||||||
transaction_validity::{TransactionValidityError, InvalidTransaction},
|
|
||||||
};
|
|
||||||
|
|
||||||
use frame_system::pallet_prelude::*;
|
|
||||||
use frame_support::pallet_prelude::*;
|
|
||||||
|
|
||||||
use pallet_transaction_payment::{Config as TpConfig, OnChargeTransaction};
|
|
||||||
|
|
||||||
use serai_primitives::*;
|
|
||||||
pub use coins_primitives as primitives;
|
|
||||||
use primitives::*;
|
|
||||||
|
|
||||||
type LiquidityTokensInstance = crate::Instance1;
|
|
||||||
|
|
||||||
#[pallet::config]
|
|
||||||
pub trait Config<I: 'static = ()>: frame_system::Config<AccountId = Public> {
|
|
||||||
type RuntimeEvent: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
|
||||||
type AllowMint: AllowMint;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pallet::genesis_config]
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
|
||||||
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
|
|
||||||
pub accounts: Vec<(T::AccountId, Balance)>,
|
|
||||||
pub _ignore: PhantomData<I>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
|
|
||||||
fn default() -> Self {
|
|
||||||
GenesisConfig { accounts: Default::default(), _ignore: Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pallet::error]
|
|
||||||
pub enum Error<T, I = ()> {
|
|
||||||
AmountOverflowed,
|
|
||||||
NotEnoughCoins,
|
|
||||||
BurnWithInstructionNotAllowed,
|
|
||||||
MintNotAllowed,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pallet::event]
|
|
||||||
#[pallet::generate_deposit(fn deposit_event)]
|
|
||||||
pub enum Event<T: Config<I>, I: 'static = ()> {
|
|
||||||
Mint { to: Public, balance: Balance },
|
|
||||||
Burn { from: Public, balance: Balance },
|
|
||||||
BurnWithInstruction { from: Public, instruction: OutInstructionWithBalance },
|
|
||||||
Transfer { from: Public, to: Public, balance: Balance },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pallet::pallet]
|
|
||||||
pub struct Pallet<T, I = ()>(_);
|
|
||||||
|
|
||||||
/// The amount of coins each account has.
|
|
||||||
// Identity is used as the second key's hasher due to it being a non-manipulatable fixed-space
|
|
||||||
// ID.
|
|
||||||
#[pallet::storage]
|
|
||||||
#[pallet::getter(fn balances)]
|
|
||||||
pub type Balances<T: Config<I>, I: 'static = ()> =
|
|
||||||
StorageDoubleMap<_, Blake2_128Concat, Public, Identity, Coin, SubstrateAmount, ValueQuery>;
|
|
||||||
|
|
||||||
/// The total supply of each coin.
|
|
||||||
// We use Identity type here again due to reasons stated in the Balances Storage.
|
|
||||||
#[pallet::storage]
|
|
||||||
#[pallet::getter(fn supply)]
|
|
||||||
pub type Supply<T: Config<I>, I: 'static = ()> =
|
|
||||||
StorageMap<_, Identity, Coin, SubstrateAmount, ValueQuery>;
|
|
||||||
|
|
||||||
#[pallet::genesis_build]
|
|
||||||
impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
|
|
||||||
fn build(&self) {
|
|
||||||
// initialize the supply of the coins
|
|
||||||
// TODO: Don't use COINS yet GenesisConfig so we can safely expand COINS
|
|
||||||
for c in &COINS {
|
|
||||||
Supply::<T, I>::set(c, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize the genesis accounts
|
|
||||||
for (account, balance) in &self.accounts {
|
|
||||||
Pallet::<T, I>::mint(*account, *balance).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pallet::hooks]
|
|
||||||
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
|
|
||||||
fn on_initialize(_: BlockNumberFor<T>) -> Weight {
|
|
||||||
// burn the fees collected previous block
|
|
||||||
let coin = Coin::Serai;
|
|
||||||
let amount = Self::balance(FEE_ACCOUNT.into(), coin);
|
|
||||||
// we can unwrap, we are not burning more then what we have
|
|
||||||
// If this errors, it'll halt the runtime however (due to being called at the start of every
|
|
||||||
// block), requiring extra care when reviewing
|
|
||||||
Self::burn_internal(FEE_ACCOUNT.into(), Balance { coin, amount }).unwrap();
|
|
||||||
Weight::zero() // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
|
||||||
/// Returns the balance of a given account for `coin`.
|
|
||||||
pub fn balance(of: Public, coin: Coin) -> Amount {
|
|
||||||
Amount(Self::balances(of, coin))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decrease_balance_internal(from: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
|
||||||
let coin = &balance.coin;
|
|
||||||
|
|
||||||
// sub amount from account
|
|
||||||
let new_amount = Self::balances(from, coin)
|
|
||||||
.checked_sub(balance.amount.0)
|
|
||||||
.ok_or(Error::<T, I>::NotEnoughCoins)?;
|
|
||||||
|
|
||||||
// save
|
|
||||||
if new_amount == 0 {
|
|
||||||
Balances::<T, I>::remove(from, coin);
|
|
||||||
} else {
|
|
||||||
Balances::<T, I>::set(from, coin, new_amount);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn increase_balance_internal(to: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
|
||||||
let coin = &balance.coin;
|
|
||||||
|
|
||||||
// add amount to account
|
|
||||||
let new_amount = Self::balances(to, coin)
|
|
||||||
.checked_add(balance.amount.0)
|
|
||||||
.ok_or(Error::<T, I>::AmountOverflowed)?;
|
|
||||||
|
|
||||||
// save
|
|
||||||
Balances::<T, I>::set(to, coin, new_amount);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mint `balance` to the given account.
|
|
||||||
///
|
|
||||||
/// Errors if any amount overflows.
|
|
||||||
pub fn mint(to: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
|
||||||
// If the coin isn't Serai, which we're always allowed to mint, and the mint isn't explicitly
|
|
||||||
// allowed, error
|
|
||||||
if !ExternalCoin::try_from(balance.coin)
|
|
||||||
.map(|coin| T::AllowMint::is_allowed(&ExternalBalance { coin, amount: balance.amount }))
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
Err(Error::<T, I>::MintNotAllowed)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the balance
|
|
||||||
Self::increase_balance_internal(to, balance)?;
|
|
||||||
|
|
||||||
// update the supply
|
|
||||||
let new_supply = Self::supply(balance.coin)
|
|
||||||
.checked_add(balance.amount.0)
|
|
||||||
.ok_or(Error::<T, I>::AmountOverflowed)?;
|
|
||||||
Supply::<T, I>::set(balance.coin, new_supply);
|
|
||||||
|
|
||||||
Self::deposit_event(Event::Mint { to, balance });
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Burn `balance` from the specified account.
|
|
||||||
fn burn_internal(from: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
|
||||||
// don't waste time if amount == 0
|
|
||||||
if balance.amount.0 == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the balance
|
|
||||||
Self::decrease_balance_internal(from, balance)?;
|
|
||||||
|
|
||||||
// update the supply
|
|
||||||
let new_supply = Self::supply(balance.coin).checked_sub(balance.amount.0).unwrap();
|
|
||||||
Supply::<T, I>::set(balance.coin, new_supply);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transfer `balance` from `from` to `to`.
|
|
||||||
pub fn transfer_internal(
|
|
||||||
from: Public,
|
|
||||||
to: Public,
|
|
||||||
balance: Balance,
|
|
||||||
) -> Result<(), Error<T, I>> {
|
|
||||||
// update balances of accounts
|
|
||||||
Self::decrease_balance_internal(from, balance)?;
|
|
||||||
Self::increase_balance_internal(to, balance)?;
|
|
||||||
Self::deposit_event(Event::Transfer { from, to, balance });
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pallet::call]
|
|
||||||
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
|
||||||
#[pallet::call_index(0)]
|
|
||||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
|
||||||
pub fn transfer(origin: OriginFor<T>, to: Public, balance: Balance) -> DispatchResult {
|
|
||||||
let from = ensure_signed(origin)?;
|
|
||||||
Self::transfer_internal(from, to, balance)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Burn `balance` from the caller.
|
|
||||||
#[pallet::call_index(1)]
|
|
||||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
|
||||||
pub fn burn(origin: OriginFor<T>, balance: Balance) -> DispatchResult {
|
|
||||||
let from = ensure_signed(origin)?;
|
|
||||||
Self::burn_internal(from, balance)?;
|
|
||||||
Self::deposit_event(Event::Burn { from, balance });
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Burn `balance` with `OutInstructionWithBalance` from the caller.
|
|
||||||
#[pallet::call_index(2)]
|
|
||||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
|
||||||
pub fn burn_with_instruction(
|
|
||||||
origin: OriginFor<T>,
|
|
||||||
instruction: OutInstructionWithBalance,
|
|
||||||
) -> DispatchResult {
|
|
||||||
if TypeId::of::<I>() == TypeId::of::<LiquidityTokensInstance>() {
|
|
||||||
Err(Error::<T, I>::BurnWithInstructionNotAllowed)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let from = ensure_signed(origin)?;
|
|
||||||
Self::burn_internal(from, instruction.balance.into())?;
|
|
||||||
Self::deposit_event(Event::BurnWithInstruction { from, instruction });
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> OnChargeTransaction<T> for Pallet<T>
|
|
||||||
where
|
|
||||||
T: TpConfig,
|
|
||||||
{
|
|
||||||
type Balance = SubstrateAmount;
|
|
||||||
type LiquidityInfo = Option<SubstrateAmount>;
|
|
||||||
|
|
||||||
fn withdraw_fee(
|
|
||||||
who: &Public,
|
|
||||||
_call: &T::RuntimeCall,
|
|
||||||
_dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
|
|
||||||
fee: Self::Balance,
|
|
||||||
_tip: Self::Balance,
|
|
||||||
) -> Result<Self::LiquidityInfo, TransactionValidityError> {
|
|
||||||
if fee == 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let balance = Balance { coin: Coin::Serai, amount: Amount(fee) };
|
|
||||||
match Self::transfer_internal(*who, FEE_ACCOUNT.into(), balance) {
|
|
||||||
Err(_) => Err(InvalidTransaction::Payment)?,
|
|
||||||
Ok(()) => Ok(Some(fee)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn correct_and_deposit_fee(
|
|
||||||
who: &Public,
|
|
||||||
_dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
|
|
||||||
_post_info: &PostDispatchInfoOf<T::RuntimeCall>,
|
|
||||||
corrected_fee: Self::Balance,
|
|
||||||
_tip: Self::Balance,
|
|
||||||
already_withdrawn: Self::LiquidityInfo,
|
|
||||||
) -> Result<(), TransactionValidityError> {
|
|
||||||
if let Some(paid) = already_withdrawn {
|
|
||||||
let refund_amount = paid.saturating_sub(corrected_fee);
|
|
||||||
let balance = Balance { coin: Coin::Serai, amount: Amount(refund_amount) };
|
|
||||||
Self::transfer_internal(FEE_ACCOUNT.into(), *who, balance)
|
|
||||||
.map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use pallet::*;
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
//! Test environment for Coins pallet.
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use frame_support::{
|
|
||||||
construct_runtime,
|
|
||||||
traits::{ConstU32, ConstU64},
|
|
||||||
};
|
|
||||||
|
|
||||||
use sp_core::{H256, sr25519::Public};
|
|
||||||
use sp_runtime::{
|
|
||||||
traits::{BlakeTwo256, IdentityLookup},
|
|
||||||
BuildStorage,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate as coins;
|
|
||||||
|
|
||||||
type Block = frame_system::mocking::MockBlock<Test>;
|
|
||||||
|
|
||||||
construct_runtime!(
|
|
||||||
pub enum Test
|
|
||||||
{
|
|
||||||
System: frame_system,
|
|
||||||
Coins: coins,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
impl frame_system::Config for Test {
|
|
||||||
type BaseCallFilter = frame_support::traits::Everything;
|
|
||||||
type BlockWeights = ();
|
|
||||||
type BlockLength = ();
|
|
||||||
type RuntimeOrigin = RuntimeOrigin;
|
|
||||||
type RuntimeCall = RuntimeCall;
|
|
||||||
type Nonce = u64;
|
|
||||||
type Hash = H256;
|
|
||||||
type Hashing = BlakeTwo256;
|
|
||||||
type AccountId = Public;
|
|
||||||
type Lookup = IdentityLookup<Self::AccountId>;
|
|
||||||
type Block = Block;
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
type BlockHashCount = ConstU64<250>;
|
|
||||||
type DbWeight = ();
|
|
||||||
type Version = ();
|
|
||||||
type PalletInfo = PalletInfo;
|
|
||||||
type AccountData = ();
|
|
||||||
type OnNewAccount = ();
|
|
||||||
type OnKilledAccount = ();
|
|
||||||
type SystemWeightInfo = ();
|
|
||||||
type SS58Prefix = ();
|
|
||||||
type OnSetCode = ();
|
|
||||||
type MaxConsumers = ConstU32<16>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config for Test {
|
|
||||||
type RuntimeEvent = RuntimeEvent;
|
|
||||||
|
|
||||||
type AllowMint = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
|
|
||||||
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
|
||||||
|
|
||||||
crate::GenesisConfig::<Test> { accounts: vec![], _ignore: Default::default() }
|
|
||||||
.assimilate_storage(&mut t)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut ext = sp_io::TestExternalities::new(t);
|
|
||||||
ext.execute_with(|| System::set_block_number(0));
|
|
||||||
ext
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "serai-coins-primitives"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Serai coins primitives"
|
|
||||||
license = "MIT"
|
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.80"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
zeroize = { version = "^1.5", features = ["derive"], optional = true }
|
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
std = ["zeroize", "borsh?/std", "serde?/std", "scale/std", "scale-info/std", "sp-runtime/std", "serai-primitives/std"]
|
|
||||||
borsh = ["dep:borsh", "serai-primitives/borsh"]
|
|
||||||
serde = ["dep:serde", "serai-primitives/serde"]
|
|
||||||
default = ["std"]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2023 Luke Parker
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
#[cfg(feature = "borsh")]
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
use scale::{Encode, Decode, MaxEncodedLen};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
|
|
||||||
use serai_primitives::{ExternalBalance, SeraiAddress, ExternalAddress, system_address};
|
|
||||||
|
|
||||||
pub const FEE_ACCOUNT: SeraiAddress = system_address(b"Coins-fees");
|
|
||||||
|
|
||||||
// TODO: Replace entirely with just Address
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct OutInstruction {
|
|
||||||
pub address: ExternalAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct OutInstructionWithBalance {
|
|
||||||
pub instruction: OutInstruction,
|
|
||||||
pub balance: ExternalBalance,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub enum Destination {
|
|
||||||
Native(SeraiAddress),
|
|
||||||
External(OutInstruction),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn address() {
|
|
||||||
use sp_runtime::traits::TrailingZeroInput;
|
|
||||||
assert_eq!(
|
|
||||||
FEE_ACCOUNT,
|
|
||||||
SeraiAddress::decode(&mut TrailingZeroInput::new(b"Coins-fees")).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
294
substrate/coins/src/lib.rs
Normal file
294
substrate/coins/src/lib.rs
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod mock;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
use serai_primitives::balance::ExternalBalance;
|
||||||
|
|
||||||
|
/// The decider for if a mint is allowed or not.
|
||||||
|
pub trait AllowMint {
|
||||||
|
/// Whether or not the mint of the specified coins is allowed.
|
||||||
|
fn is_allowed(balance: &ExternalBalance) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An `AllowMint` implementor which always returns true.
|
||||||
|
pub struct AlwaysAllowMint;
|
||||||
|
impl AllowMint for AlwaysAllowMint {
|
||||||
|
fn is_allowed(_: &ExternalBalance) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
#[frame_support::pallet]
|
||||||
|
mod pallet {
|
||||||
|
use core::any::TypeId;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use sp_core::sr25519::Public;
|
||||||
|
|
||||||
|
use frame_system::pallet_prelude::*;
|
||||||
|
use frame_support::pallet_prelude::*;
|
||||||
|
|
||||||
|
use serai_primitives::{coin::*, balance::*, instructions::OutInstructionWithBalance};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// The instance used to represent coins on the Serai network.
|
||||||
|
///
|
||||||
|
/// This would either be SRI itself or the sriXYZ coins swappable via pools.
|
||||||
|
pub struct CoinsInstance;
|
||||||
|
/// The instance used to represent liquidity tokens on the Serai network.
|
||||||
|
///
|
||||||
|
/// Coin::XYZ would be considered as the liquidity token for the Coin::SRI - Coin::XYZ pool.
|
||||||
|
pub struct LiquidityTokensInstance;
|
||||||
|
|
||||||
|
/// The configuration of this pallet.
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config<I: 'static = ()>: frame_system::Config<AccountId = Public> {
|
||||||
|
/// The event type.
|
||||||
|
type RuntimeEvent: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||||
|
/// What decides if mints are allowed.
|
||||||
|
type AllowMint: AllowMint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The genesis state to use for this pallet.
|
||||||
|
#[pallet::genesis_config]
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
|
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
|
||||||
|
/// The balances to initiate the state with.
|
||||||
|
///
|
||||||
|
/// This is useful for test networks where it's desirable to have some coins available
|
||||||
|
/// immediately.
|
||||||
|
pub accounts: Vec<(T::AccountId, Balance)>,
|
||||||
|
/// PhantomData to bind `I`.
|
||||||
|
pub _instance: PhantomData<I>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
|
||||||
|
fn default() -> Self {
|
||||||
|
GenesisConfig { accounts: Default::default(), _instance: Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An error incurred.
|
||||||
|
#[pallet::error]
|
||||||
|
pub enum Error<T, I = ()> {
|
||||||
|
/// The mint wasn't allowed.
|
||||||
|
MintNotAllowed,
|
||||||
|
/// The amount an account had overflowed.
|
||||||
|
AmountOverflowed,
|
||||||
|
/// The account didn't have enough coins for this operation.
|
||||||
|
NotEnoughCoins,
|
||||||
|
/// An Instruction was specified with a Burn when that's unsupported.
|
||||||
|
BurnWithInstructionNotAllowed,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An event emitted.
|
||||||
|
#[pallet::event]
|
||||||
|
#[pallet::generate_deposit(fn deposit_event)]
|
||||||
|
pub enum Event<T: Config<I>, I: 'static = ()> {
|
||||||
|
/// Coins were minted.
|
||||||
|
Mint {
|
||||||
|
/// The account minted to.
|
||||||
|
to: Public,
|
||||||
|
/// The balance minted.
|
||||||
|
balance: Balance,
|
||||||
|
},
|
||||||
|
/// Coins were transferred.
|
||||||
|
Transfer {
|
||||||
|
/// The account transferred from.
|
||||||
|
from: Public,
|
||||||
|
/// The account transferred to.
|
||||||
|
to: Public,
|
||||||
|
/// The balance transferred.
|
||||||
|
balance: Balance,
|
||||||
|
},
|
||||||
|
/// Coins were burnt.
|
||||||
|
Burn {
|
||||||
|
/// The account burnt from.
|
||||||
|
from: Public,
|
||||||
|
/// The balance burnt.
|
||||||
|
balance: Balance,
|
||||||
|
},
|
||||||
|
/// Coins were burnt with an instruction.
|
||||||
|
BurnWithInstruction {
|
||||||
|
/// The account burnt from.
|
||||||
|
from: Public,
|
||||||
|
/// The instruction, and associated balance.
|
||||||
|
instruction: OutInstructionWithBalance,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Pallet struct.
|
||||||
|
#[pallet::pallet]
|
||||||
|
pub struct Pallet<T, I = ()>(_);
|
||||||
|
|
||||||
|
/// The amount of coins each account has.
|
||||||
|
// Identity is used as the second key's hasher due to Coin being a small, fixed-space ID.
|
||||||
|
#[pallet::storage]
|
||||||
|
type Balances<T: Config<I>, I: 'static = ()> =
|
||||||
|
StorageDoubleMap<_, Blake2_128Concat, Public, Identity, Coin, Amount, ValueQuery>;
|
||||||
|
|
||||||
|
/// The total supply of each coin.
|
||||||
|
#[pallet::storage]
|
||||||
|
type Supply<T: Config<I>, I: 'static = ()> = StorageMap<_, Identity, Coin, Amount, ValueQuery>;
|
||||||
|
|
||||||
|
#[pallet::genesis_build]
|
||||||
|
impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
|
||||||
|
fn build(&self) {
|
||||||
|
// initialize the genesis accounts
|
||||||
|
for (account, balance) in &self.accounts {
|
||||||
|
Pallet::<T, I>::mint(*account, *balance).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||||
|
/// Returns the balance of `coin` for the specified account.
|
||||||
|
pub fn balance(
|
||||||
|
of: impl scale::EncodeLike<Public>,
|
||||||
|
coin: impl scale::EncodeLike<Coin>,
|
||||||
|
) -> Amount {
|
||||||
|
Balances::<T, I>::get(of, coin)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the supply of `coin`.
|
||||||
|
pub fn supply(coin: impl scale::EncodeLike<Coin>) -> Amount {
|
||||||
|
Supply::<T, I>::get(coin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrease_balance_internal(from: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||||
|
let coin = &balance.coin;
|
||||||
|
|
||||||
|
// sub amount from account
|
||||||
|
let new_amount =
|
||||||
|
(Self::balance(from, coin) - balance.amount).ok_or(Error::<T, I>::NotEnoughCoins)?;
|
||||||
|
|
||||||
|
// save
|
||||||
|
if new_amount == Amount(0) {
|
||||||
|
Balances::<T, I>::remove(from, coin);
|
||||||
|
} else {
|
||||||
|
Balances::<T, I>::set(from, coin, new_amount);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increase_balance_internal(to: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||||
|
let coin = &balance.coin;
|
||||||
|
|
||||||
|
// add amount to account
|
||||||
|
let new_amount =
|
||||||
|
(Self::balance(to, coin) + balance.amount).ok_or(Error::<T, I>::AmountOverflowed)?;
|
||||||
|
|
||||||
|
// save
|
||||||
|
Balances::<T, I>::set(to, coin, new_amount);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mint `balance` to the given account.
|
||||||
|
///
|
||||||
|
/// Errors if any amount overflows.
|
||||||
|
pub fn mint(to: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||||
|
{
|
||||||
|
// If this is an external coin, check if we can mint it
|
||||||
|
let external_balance = ExternalBalance::try_from(balance);
|
||||||
|
let can_mint_external = external_balance.as_ref().map(T::AllowMint::is_allowed);
|
||||||
|
// If it was native to the Serai network, we can always mint it
|
||||||
|
let can_mint = can_mint_external.unwrap_or(true);
|
||||||
|
if !can_mint {
|
||||||
|
Err(Error::<T, I>::MintNotAllowed)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the balance
|
||||||
|
Self::increase_balance_internal(to, balance)?;
|
||||||
|
|
||||||
|
// update the supply
|
||||||
|
let new_supply = (Supply::<T, I>::get(balance.coin) + balance.amount)
|
||||||
|
.ok_or(Error::<T, I>::AmountOverflowed)?;
|
||||||
|
Supply::<T, I>::set(balance.coin, new_supply);
|
||||||
|
|
||||||
|
Self::deposit_event(Event::Mint { to, balance });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Burn `balance` from the specified account.
|
||||||
|
fn burn_internal(from: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||||
|
// update the balance
|
||||||
|
Self::decrease_balance_internal(from, balance)?;
|
||||||
|
|
||||||
|
// update the supply
|
||||||
|
Supply::<T, I>::mutate(balance.coin, |supply| {
|
||||||
|
// We can unwrap here as we're burning an amount legitimately in the system (per successful
|
||||||
|
// decrease), so the supply must be greater than this value
|
||||||
|
let new_supply = (*supply - balance.amount).unwrap();
|
||||||
|
*supply = new_supply;
|
||||||
|
});
|
||||||
|
|
||||||
|
// We don't emit the event here, but rather at the call-site, due to being unsure if we
|
||||||
|
// should emit `Burn` or `BurnWithInstruction`
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transfer `balance` from `from` to `to`.
|
||||||
|
fn transfer_internal(from: Public, to: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||||
|
// update balances of accounts
|
||||||
|
Self::decrease_balance_internal(from, balance)?;
|
||||||
|
Self::increase_balance_internal(to, balance)?;
|
||||||
|
Self::deposit_event(Event::Transfer { from, to, balance });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::call]
|
||||||
|
impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||||
|
/// Transfer `balance` from the signer to `to`.
|
||||||
|
#[pallet::call_index(0)]
|
||||||
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||||
|
pub fn transfer(origin: OriginFor<T>, to: Public, balance: Balance) -> DispatchResult {
|
||||||
|
let from = ensure_signed(origin)?;
|
||||||
|
Self::transfer_internal(from, to, balance)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Burn `balance` from the signer.
|
||||||
|
#[pallet::call_index(1)]
|
||||||
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||||
|
pub fn burn(origin: OriginFor<T>, balance: Balance) -> DispatchResult {
|
||||||
|
let from = ensure_signed(origin)?;
|
||||||
|
Self::burn_internal(from, balance)?;
|
||||||
|
Self::deposit_event(Event::Burn { from, balance });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Burn `balance` from the signer with `instruction`.
|
||||||
|
#[pallet::call_index(2)]
|
||||||
|
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||||
|
pub fn burn_with_instruction(
|
||||||
|
origin: OriginFor<T>,
|
||||||
|
instruction: OutInstructionWithBalance,
|
||||||
|
) -> DispatchResult {
|
||||||
|
// Only allow specifying an instruction if this is Coins, not LiquidityTokens
|
||||||
|
if TypeId::of::<I>() != TypeId::of::<CoinsInstance>() {
|
||||||
|
Err(Error::<T, I>::BurnWithInstructionNotAllowed)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let from = ensure_signed(origin)?;
|
||||||
|
Self::burn_internal(from, instruction.balance.into())?;
|
||||||
|
Self::deposit_event(Event::BurnWithInstruction { from, instruction });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use pallet::*;
|
||||||
37
substrate/coins/src/mock.rs
Normal file
37
substrate/coins/src/mock.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//! Test environment for Coins pallet.
|
||||||
|
|
||||||
|
use sp_runtime::BuildStorage;
|
||||||
|
|
||||||
|
use frame_support::{derive_impl, construct_runtime};
|
||||||
|
|
||||||
|
use crate::{self as coins, CoinsInstance};
|
||||||
|
|
||||||
|
construct_runtime!(
|
||||||
|
pub enum Test
|
||||||
|
{
|
||||||
|
System: frame_system,
|
||||||
|
Coins: coins::<CoinsInstance>,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||||
|
impl frame_system::Config for Test {
|
||||||
|
type AccountId = sp_core::sr25519::Public;
|
||||||
|
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
|
||||||
|
type Block = frame_system::mocking::MockBlock<Test>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::Config<CoinsInstance> for Test {
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
type AllowMint = crate::AlwaysAllowMint;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
|
||||||
|
let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||||
|
|
||||||
|
crate::GenesisConfig::<Test, CoinsInstance> { accounts: vec![], _instance: Default::default() }
|
||||||
|
.assimilate_storage(&mut storage)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
storage.into()
|
||||||
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
use crate::{mock::*, primitives::*};
|
use sp_core::{Pair as _, sr25519::Pair};
|
||||||
|
|
||||||
use frame_system::RawOrigin;
|
use frame_system::RawOrigin;
|
||||||
use sp_core::Pair;
|
|
||||||
|
|
||||||
use serai_primitives::*;
|
use serai_primitives::{coin::*, balance::*, address::*, instructions::*};
|
||||||
|
|
||||||
pub type CoinsEvent = crate::Event<Test, ()>;
|
use crate::mock::*;
|
||||||
|
|
||||||
|
pub type CoinsEvent = crate::Event<Test, crate::CoinsInstance>;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mint() {
|
fn mint() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
// minting u64::MAX should work
|
// minting u64::MAX should work
|
||||||
let coin = Coin::Serai;
|
let coin = Coin::Serai;
|
||||||
let to = insecure_pair_from_name("random1").public();
|
let to = Pair::generate().0.public();
|
||||||
let balance = Balance { coin, amount: Amount(u64::MAX) };
|
let balance = Balance { coin, amount: Amount(u64::MAX) };
|
||||||
|
|
||||||
Coins::mint(to, balance).unwrap();
|
Coins::mint(to, balance).unwrap();
|
||||||
@@ -22,7 +22,7 @@ fn mint() {
|
|||||||
assert!(Coins::mint(to, Balance { coin, amount: Amount(1) }).is_err());
|
assert!(Coins::mint(to, Balance { coin, amount: Amount(1) }).is_err());
|
||||||
|
|
||||||
// supply now should be equal to sum of the accounts balance sum
|
// supply now should be equal to sum of the accounts balance sum
|
||||||
assert_eq!(Coins::supply(coin), balance.amount.0);
|
assert_eq!(Coins::supply(coin), balance.amount);
|
||||||
|
|
||||||
// test events
|
// test events
|
||||||
let mint_events = System::events()
|
let mint_events = System::events()
|
||||||
@@ -49,19 +49,19 @@ fn burn_with_instruction() {
|
|||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
// mint some coin
|
// mint some coin
|
||||||
let coin = Coin::External(ExternalCoin::Bitcoin);
|
let coin = Coin::External(ExternalCoin::Bitcoin);
|
||||||
let to = insecure_pair_from_name("random1").public();
|
let to = Pair::generate().0.public();
|
||||||
let balance = Balance { coin, amount: Amount(10 * 10u64.pow(coin.decimals())) };
|
let balance = Balance { coin, amount: Amount(10 * 10u64.pow(coin.decimals())) };
|
||||||
|
|
||||||
Coins::mint(to, balance).unwrap();
|
Coins::mint(to, balance).unwrap();
|
||||||
assert_eq!(Coins::balance(to, coin), balance.amount);
|
assert_eq!(Coins::balance(to, coin), balance.amount);
|
||||||
assert_eq!(Coins::supply(coin), balance.amount.0);
|
assert_eq!(Coins::supply(coin), balance.amount);
|
||||||
|
|
||||||
// we shouldn't be able to burn more than what we have
|
// we shouldn't be able to burn more than what we have
|
||||||
let mut instruction = OutInstructionWithBalance {
|
let mut instruction = OutInstructionWithBalance {
|
||||||
instruction: OutInstruction { address: ExternalAddress::new(vec![]).unwrap() },
|
instruction: OutInstruction::Transfer(ExternalAddress::try_from(vec![]).unwrap()),
|
||||||
balance: ExternalBalance {
|
balance: ExternalBalance {
|
||||||
coin: coin.try_into().unwrap(),
|
coin: coin.try_into().unwrap(),
|
||||||
amount: Amount(balance.amount.0 + 1),
|
amount: (balance.amount + Amount(1)).unwrap(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert!(
|
assert!(
|
||||||
@@ -74,7 +74,7 @@ fn burn_with_instruction() {
|
|||||||
|
|
||||||
// balance & supply now should be back to 0
|
// balance & supply now should be back to 0
|
||||||
assert_eq!(Coins::balance(to, coin), Amount(0));
|
assert_eq!(Coins::balance(to, coin), Amount(0));
|
||||||
assert_eq!(Coins::supply(coin), 0);
|
assert_eq!(Coins::supply(coin), Amount(0));
|
||||||
|
|
||||||
let burn_events = System::events()
|
let burn_events = System::events()
|
||||||
.iter()
|
.iter()
|
||||||
@@ -100,19 +100,19 @@ fn transfer() {
|
|||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
// mint some coin
|
// mint some coin
|
||||||
let coin = Coin::External(ExternalCoin::Bitcoin);
|
let coin = Coin::External(ExternalCoin::Bitcoin);
|
||||||
let from = insecure_pair_from_name("random1").public();
|
let from = Pair::generate().0.public();
|
||||||
let balance = Balance { coin, amount: Amount(10 * 10u64.pow(coin.decimals())) };
|
let balance = Balance { coin, amount: Amount(10 * 10u64.pow(coin.decimals())) };
|
||||||
|
|
||||||
Coins::mint(from, balance).unwrap();
|
Coins::mint(from, balance).unwrap();
|
||||||
assert_eq!(Coins::balance(from, coin), balance.amount);
|
assert_eq!(Coins::balance(from, coin), balance.amount);
|
||||||
assert_eq!(Coins::supply(coin), balance.amount.0);
|
assert_eq!(Coins::supply(coin), balance.amount);
|
||||||
|
|
||||||
// we can't send more than what we have
|
// we can't send more than what we have
|
||||||
let to = insecure_pair_from_name("random2").public();
|
let to = Pair::generate().0.public();
|
||||||
assert!(Coins::transfer(
|
assert!(Coins::transfer(
|
||||||
RawOrigin::Signed(from).into(),
|
RawOrigin::Signed(from).into(),
|
||||||
to,
|
to,
|
||||||
Balance { coin, amount: Amount(balance.amount.0 + 1) }
|
Balance { coin, amount: (balance.amount + Amount(1)).unwrap() }
|
||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
|
|
||||||
@@ -124,6 +124,6 @@ fn transfer() {
|
|||||||
assert_eq!(Coins::balance(to, coin), balance.amount);
|
assert_eq!(Coins::balance(to, coin), balance.amount);
|
||||||
|
|
||||||
// supply shouldn't change
|
// supply shouldn't change
|
||||||
assert_eq!(Coins::supply(coin), balance.amount.0);
|
assert_eq!(Coins::supply(coin), balance.amount);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ name = "serai-dex-pallet"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "DEX pallet for Serai"
|
description = "DEX pallet for Serai"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/dex/pallet"
|
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/dex"
|
||||||
authors = ["Parity Technologies <admin@parity.io>, Akil Demir <akildemir72@gmail.com>"]
|
authors = ["Parity Technologies <admin@parity.io>, Akil Demir <akildemir72@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
@@ -22,19 +22,19 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
|
scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
|
||||||
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
|
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-api = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate", default-features = false, optional = true }
|
frame-benchmarking = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, optional = true }
|
||||||
|
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../primitives", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use frame_benchmarking::{benchmarks, whitelisted_caller};
|
use frame_benchmarking::{benchmarks, whitelisted_caller};
|
||||||
use frame_support::{assert_ok, storage::bounded_vec::BoundedVec};
|
use frame_support::{assert_ok, storage::bounded::BoundedVec};
|
||||||
use frame_system::RawOrigin as SystemOrigin;
|
use frame_system::RawOrigin as SystemOrigin;
|
||||||
|
|
||||||
use sp_runtime::traits::StaticLookup;
|
use sp_runtime::traits::StaticLookup;
|
||||||
77
substrate/economic-security/Cargo.toml
Normal file
77
substrate/economic-security/Cargo.toml
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
[package]
|
||||||
|
name = "serai-economic-security-pallet"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Economic Security pallet for Serai"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/economic-security"
|
||||||
|
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.80"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = ["scale", "scale-info"]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
|
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
|
||||||
|
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
|
||||||
|
|
||||||
|
serai-primitives = { path = "../primitives", default-features = false }
|
||||||
|
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pallet-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
pallet-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
pallet-timestamp = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
|
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }
|
||||||
|
|
||||||
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = [
|
||||||
|
"scale/std",
|
||||||
|
"scale-info/std",
|
||||||
|
|
||||||
|
"frame-system/std",
|
||||||
|
"frame-support/std",
|
||||||
|
|
||||||
|
"sp-io/std",
|
||||||
|
"sp-core/std",
|
||||||
|
"sp-consensus-babe/std",
|
||||||
|
|
||||||
|
"dex-pallet/std",
|
||||||
|
"coins-pallet/std",
|
||||||
|
"validator-sets-pallet/std",
|
||||||
|
|
||||||
|
"serai-primitives/std",
|
||||||
|
|
||||||
|
"pallet-babe/std",
|
||||||
|
"pallet-grandpa/std",
|
||||||
|
"pallet-timestamp/std",
|
||||||
|
]
|
||||||
|
|
||||||
|
try-runtime = [
|
||||||
|
"frame-system/try-runtime",
|
||||||
|
"frame-support/try-runtime",
|
||||||
|
|
||||||
|
"sp-runtime/try-runtime",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
default = ["std"]
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "serai-economic-security-pallet"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Economic Security pallet for Serai"
|
|
||||||
license = "AGPL-3.0-only"
|
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/economic-security/pallet"
|
|
||||||
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.80"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
|
||||||
ignored = ["scale", "scale-info"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
pallet-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
pallet-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
pallet-timestamp = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../../validator-sets/pallet", default-features = false }
|
|
||||||
|
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
std = [
|
|
||||||
"scale/std",
|
|
||||||
"scale-info/std",
|
|
||||||
|
|
||||||
"frame-system/std",
|
|
||||||
"frame-support/std",
|
|
||||||
|
|
||||||
"sp-io/std",
|
|
||||||
"sp-core/std",
|
|
||||||
"sp-consensus-babe/std",
|
|
||||||
|
|
||||||
"dex-pallet/std",
|
|
||||||
"coins-pallet/std",
|
|
||||||
"validator-sets-pallet/std",
|
|
||||||
|
|
||||||
"serai-primitives/std",
|
|
||||||
|
|
||||||
"pallet-babe/std",
|
|
||||||
"pallet-grandpa/std",
|
|
||||||
"pallet-timestamp/std",
|
|
||||||
]
|
|
||||||
|
|
||||||
try-runtime = [
|
|
||||||
"frame-system/try-runtime",
|
|
||||||
"frame-support/try-runtime",
|
|
||||||
|
|
||||||
"sp-runtime/try-runtime",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
default = ["std"]
|
|
||||||
@@ -3,7 +3,7 @@ name = "serai-emissions-pallet"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Emissions pallet for Serai"
|
description = "Emissions pallet for Serai"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/emissions/pallet"
|
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/emissions"
|
||||||
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
@@ -22,22 +22,20 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
|
||||||
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../../validator-sets/pallet", default-features = false }
|
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }
|
||||||
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
|
||||||
genesis-liquidity-pallet = { package = "serai-genesis-liquidity-pallet", path = "../../genesis-liquidity/pallet", default-features = false }
|
genesis-liquidity-pallet = { package = "serai-genesis-liquidity-pallet", path = "../genesis-liquidity", default-features = false }
|
||||||
|
|
||||||
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../../economic-security/pallet", default-features = false }
|
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../economic-security", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../primitives", default-features = false }
|
||||||
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../validator-sets/primitives", default-features = false }
|
|
||||||
emissions-primitives = { package = "serai-emissions-primitives", path = "../primitives", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
@@ -58,8 +56,6 @@ std = [
|
|||||||
"economic-security-pallet/std",
|
"economic-security-pallet/std",
|
||||||
|
|
||||||
"serai-primitives/std",
|
"serai-primitives/std",
|
||||||
"emissions-primitives/std",
|
|
||||||
]
|
]
|
||||||
fast-epoch = []
|
|
||||||
try-runtime = [] # TODO
|
try-runtime = [] # TODO
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "serai-emissions-primitives"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Serai emissions primitives"
|
|
||||||
license = "MIT"
|
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/emissions/primitives"
|
|
||||||
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.80"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
std = ["serai-primitives/std"]
|
|
||||||
default = ["std"]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Luke Parker
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
use serai_primitives::{DAYS, YEARS, SeraiAddress, system_address};
|
|
||||||
|
|
||||||
// Protocol owned liquidity account.
|
|
||||||
pub const POL_ACCOUNT: SeraiAddress = system_address(b"Serai-protocol_owned_liquidity");
|
|
||||||
|
|
||||||
/// INITIAL_REWARD = 100,000 SRI / BLOCKS_PER_DAY for 60 days
|
|
||||||
pub const INITIAL_REWARD_PER_BLOCK: u64 = (100_000 * 10u64.pow(8)) / DAYS;
|
|
||||||
|
|
||||||
/// REWARD = 20M SRI / BLOCKS_PER_YEAR
|
|
||||||
pub const REWARD_PER_BLOCK: u64 = (20_000_000 * 10u64.pow(8)) / YEARS;
|
|
||||||
|
|
||||||
/// 20% of all stake desired to be for Serai network
|
|
||||||
pub const SERAI_VALIDATORS_DESIRED_PERCENTAGE: u64 = 20;
|
|
||||||
|
|
||||||
/// Desired unused capacity ratio for a network assuming capacity is 10,000.
|
|
||||||
pub const DESIRED_DISTRIBUTION: u64 = 1_000;
|
|
||||||
|
|
||||||
/// Percentage scale for the validator vs. pool reward distribution.
|
|
||||||
pub const ACCURACY_MULTIPLIER: u64 = 10_000;
|
|
||||||
|
|
||||||
/// The block to target for economic security
|
|
||||||
pub const SECURE_BY: u64 = YEARS;
|
|
||||||
62
substrate/genesis-liquidity/Cargo.toml
Normal file
62
substrate/genesis-liquidity/Cargo.toml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
[package]
|
||||||
|
name = "serai-genesis-liquidity-pallet"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Genesis liquidity pallet for Serai"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/genesis-liquidity"
|
||||||
|
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.80"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = ["scale", "scale-info"]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-application-crypto = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
|
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
|
||||||
|
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
|
||||||
|
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }
|
||||||
|
|
||||||
|
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../economic-security", default-features = false }
|
||||||
|
|
||||||
|
serai-primitives = { path = "../primitives", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = [
|
||||||
|
"scale/std",
|
||||||
|
"scale-info/std",
|
||||||
|
|
||||||
|
"frame-system/std",
|
||||||
|
"frame-support/std",
|
||||||
|
|
||||||
|
"sp-std/std",
|
||||||
|
"sp-core/std",
|
||||||
|
"sp-application-crypto/std",
|
||||||
|
|
||||||
|
"coins-pallet/std",
|
||||||
|
"dex-pallet/std",
|
||||||
|
"validator-sets-pallet/std",
|
||||||
|
|
||||||
|
"economic-security-pallet/std",
|
||||||
|
|
||||||
|
"serai-primitives/std",
|
||||||
|
]
|
||||||
|
try-runtime = [] # TODO
|
||||||
|
|
||||||
|
default = ["std"]
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "serai-genesis-liquidity-pallet"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Genesis liquidity pallet for Serai"
|
|
||||||
license = "AGPL-3.0-only"
|
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/genesis-liquidity/pallet"
|
|
||||||
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.80"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
|
||||||
ignored = ["scale", "scale-info"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
|
||||||
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../../validator-sets/pallet", default-features = false }
|
|
||||||
|
|
||||||
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../../economic-security/pallet", default-features = false }
|
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
|
||||||
genesis-liquidity-primitives = { package = "serai-genesis-liquidity-primitives", path = "../primitives", default-features = false }
|
|
||||||
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../validator-sets/primitives", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
std = [
|
|
||||||
"scale/std",
|
|
||||||
"scale-info/std",
|
|
||||||
|
|
||||||
"frame-system/std",
|
|
||||||
"frame-support/std",
|
|
||||||
|
|
||||||
"sp-std/std",
|
|
||||||
"sp-core/std",
|
|
||||||
"sp-application-crypto/std",
|
|
||||||
|
|
||||||
"coins-pallet/std",
|
|
||||||
"dex-pallet/std",
|
|
||||||
"validator-sets-pallet/std",
|
|
||||||
|
|
||||||
"economic-security-pallet/std",
|
|
||||||
|
|
||||||
"serai-primitives/std",
|
|
||||||
"genesis-liquidity-primitives/std",
|
|
||||||
"validator-sets-primitives/std",
|
|
||||||
]
|
|
||||||
try-runtime = [] # TODO
|
|
||||||
fast-epoch = []
|
|
||||||
|
|
||||||
default = ["std"]
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "serai-genesis-liquidity-primitives"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Serai genesis liquidity primitives"
|
|
||||||
license = "MIT"
|
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/genesis-liquidity/primitives"
|
|
||||||
authors = ["Akil Demir <akildemir72@gmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.80"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
zeroize = { version = "^1.5", features = ["derive"], optional = true }
|
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
|
||||||
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../validator-sets/primitives", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
std = [
|
|
||||||
"zeroize",
|
|
||||||
"scale/std",
|
|
||||||
"borsh?/std",
|
|
||||||
"serde?/std",
|
|
||||||
"scale-info/std",
|
|
||||||
|
|
||||||
"serai-primitives/std",
|
|
||||||
"validator-sets-primitives/std",
|
|
||||||
|
|
||||||
"sp-std/std"
|
|
||||||
]
|
|
||||||
default = ["std"]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Luke Parker
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
#[cfg(feature = "borsh")]
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
use sp_std::vec::Vec;
|
|
||||||
|
|
||||||
use scale::{Encode, Decode, MaxEncodedLen};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
|
|
||||||
use serai_primitives::*;
|
|
||||||
use validator_sets_primitives::ValidatorSet;
|
|
||||||
|
|
||||||
pub const INITIAL_GENESIS_LP_SHARES: u64 = 10_000;
|
|
||||||
|
|
||||||
// This is the account to hold and manage the genesis liquidity.
|
|
||||||
pub const GENESIS_LIQUIDITY_ACCOUNT: SeraiAddress = system_address(b"GenesisLiquidity-account");
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct Values {
|
|
||||||
pub monero: u64,
|
|
||||||
pub ether: u64,
|
|
||||||
pub dai: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct LiquidityAmount {
|
|
||||||
pub shares: u64,
|
|
||||||
pub coins: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LiquidityAmount {
|
|
||||||
pub fn zero() -> Self {
|
|
||||||
LiquidityAmount { shares: 0, coins: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The message for the oraclize_values signature.
|
|
||||||
pub fn oraclize_values_message(set: &ValidatorSet, values: &Values) -> Vec<u8> {
|
|
||||||
(b"GenesisLiquidity-oraclize_values", set, values).encode()
|
|
||||||
}
|
|
||||||
88
substrate/in-instructions/Cargo.toml
Normal file
88
substrate/in-instructions/Cargo.toml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
[package]
|
||||||
|
name = "serai-in-instructions-pallet"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Execute calls via In Instructions from unsigned transactions"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/genesis-liquidity"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
rust-version = "1.80"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = ["scale", "scale-info"]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitvec = { version = "1", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
|
||||||
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-application-crypto = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
|
serai-primitives = { path = "../primitives", default-features = false }
|
||||||
|
|
||||||
|
coins-pallet = { package = "serai-coins-pallet", path = "../coins", default-features = false }
|
||||||
|
dex-pallet = { package = "serai-dex-pallet", path = "../dex", default-features = false }
|
||||||
|
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../validator-sets", default-features = false }
|
||||||
|
genesis-liquidity-pallet = { package = "serai-genesis-liquidity-pallet", path = "../genesis-liquidity", default-features = false }
|
||||||
|
emissions-pallet = { package = "serai-emissions-pallet", path = "../emissions", default-features = false }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pallet-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
pallet-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
pallet-timestamp = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
|
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../economic-security", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = [
|
||||||
|
"scale/std",
|
||||||
|
"scale-info/std",
|
||||||
|
|
||||||
|
"sp-std/std",
|
||||||
|
"sp-application-crypto/std",
|
||||||
|
"sp-io/std",
|
||||||
|
"sp-runtime/std",
|
||||||
|
"sp-core/std",
|
||||||
|
|
||||||
|
"frame-system/std",
|
||||||
|
"frame-support/std",
|
||||||
|
|
||||||
|
"serai-primitives/std",
|
||||||
|
|
||||||
|
"coins-pallet/std",
|
||||||
|
"dex-pallet/std",
|
||||||
|
"validator-sets-pallet/std",
|
||||||
|
"genesis-liquidity-pallet/std",
|
||||||
|
"emissions-pallet/std",
|
||||||
|
|
||||||
|
"economic-security-pallet/std",
|
||||||
|
|
||||||
|
"pallet-babe/std",
|
||||||
|
"pallet-grandpa/std",
|
||||||
|
"pallet-timestamp/std",
|
||||||
|
]
|
||||||
|
|
||||||
|
try-runtime = [
|
||||||
|
"frame-system/try-runtime",
|
||||||
|
"frame-support/try-runtime",
|
||||||
|
|
||||||
|
"sp-runtime/try-runtime",
|
||||||
|
]
|
||||||
|
|
||||||
|
default = ["std"]
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "serai-in-instructions-pallet"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Execute calls via In Instructions from unsigned transactions"
|
|
||||||
license = "AGPL-3.0-only"
|
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
rust-version = "1.80"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
|
||||||
ignored = ["scale", "scale-info"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bitvec = { version = "1", default-features = false, features = ["alloc"] }
|
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
|
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
|
||||||
in-instructions-primitives = { package = "serai-in-instructions-primitives", path = "../primitives", default-features = false }
|
|
||||||
|
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
|
||||||
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
|
||||||
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../../validator-sets/pallet", default-features = false }
|
|
||||||
genesis-liquidity-pallet = { package = "serai-genesis-liquidity-pallet", path = "../../genesis-liquidity/pallet", default-features = false }
|
|
||||||
emissions-pallet = { package = "serai-emissions-pallet", path = "../../emissions/pallet", default-features = false }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
pallet-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
pallet-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
pallet-timestamp = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../../economic-security/pallet", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
std = [
|
|
||||||
"scale/std",
|
|
||||||
"scale-info/std",
|
|
||||||
|
|
||||||
"sp-std/std",
|
|
||||||
"sp-application-crypto/std",
|
|
||||||
"sp-io/std",
|
|
||||||
"sp-runtime/std",
|
|
||||||
"sp-core/std",
|
|
||||||
|
|
||||||
"frame-system/std",
|
|
||||||
"frame-support/std",
|
|
||||||
|
|
||||||
"serai-primitives/std",
|
|
||||||
"in-instructions-primitives/std",
|
|
||||||
|
|
||||||
"coins-pallet/std",
|
|
||||||
"dex-pallet/std",
|
|
||||||
"validator-sets-pallet/std",
|
|
||||||
"genesis-liquidity-pallet/std",
|
|
||||||
"emissions-pallet/std",
|
|
||||||
|
|
||||||
"economic-security-pallet/std",
|
|
||||||
|
|
||||||
"pallet-babe/std",
|
|
||||||
"pallet-grandpa/std",
|
|
||||||
"pallet-timestamp/std",
|
|
||||||
]
|
|
||||||
|
|
||||||
try-runtime = [
|
|
||||||
"frame-system/try-runtime",
|
|
||||||
"frame-support/try-runtime",
|
|
||||||
|
|
||||||
"sp-runtime/try-runtime",
|
|
||||||
]
|
|
||||||
|
|
||||||
default = ["std"]
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "serai-in-instructions-primitives"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Serai instructions library, enabling encoding and decoding"
|
|
||||||
license = "MIT"
|
|
||||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.80"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
zeroize = { version = "^1.5", features = ["derive"], optional = true }
|
|
||||||
|
|
||||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
|
||||||
coins-primitives = { package = "serai-coins-primitives", path = "../../coins/primitives", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
std = [
|
|
||||||
"zeroize",
|
|
||||||
|
|
||||||
"borsh?/std",
|
|
||||||
"serde?/std",
|
|
||||||
|
|
||||||
"scale/std",
|
|
||||||
"scale-info/std",
|
|
||||||
|
|
||||||
"sp-std/std",
|
|
||||||
"sp-application-crypto/std",
|
|
||||||
"sp-runtime/std",
|
|
||||||
|
|
||||||
"serai-primitives/std",
|
|
||||||
"coins-primitives/std",
|
|
||||||
]
|
|
||||||
borsh = ["dep:borsh", "serai-primitives/borsh", "coins-primitives/borsh"]
|
|
||||||
serde = ["dep:serde", "serai-primitives/serde", "coins-primitives/serde"]
|
|
||||||
default = ["std"]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022-2023 Luke Parker
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
#[cfg(feature = "borsh")]
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
use scale::{Encode, Decode, MaxEncodedLen};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
|
|
||||||
use sp_application_crypto::sr25519::Signature;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use sp_std::vec::Vec;
|
|
||||||
use sp_runtime::RuntimeDebug;
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
use serai_primitives::{BlockHash, ExternalNetworkId, NetworkId, ExternalBalance, Balance, SeraiAddress, ExternalAddress, system_address};
|
|
||||||
|
|
||||||
mod shorthand;
|
|
||||||
pub use shorthand::*;
|
|
||||||
|
|
||||||
pub const MAX_BATCH_SIZE: usize = 25_000; // ~25kb
|
|
||||||
|
|
||||||
// This is the account which will be the origin for any dispatched `InInstruction`s.
|
|
||||||
pub const IN_INSTRUCTION_EXECUTOR: SeraiAddress = system_address(b"InInstructions-executor");
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub enum OutAddress {
|
|
||||||
Serai(SeraiAddress),
|
|
||||||
External(ExternalAddress),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutAddress {
|
|
||||||
pub fn is_native(&self) -> bool {
|
|
||||||
matches!(self, Self::Serai(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_native(self) -> Option<SeraiAddress> {
|
|
||||||
match self {
|
|
||||||
Self::Serai(addr) => Some(addr),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_external(self) -> Option<ExternalAddress> {
|
|
||||||
match self {
|
|
||||||
Self::External(addr) => Some(addr),
|
|
||||||
Self::Serai(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub enum DexCall {
|
|
||||||
// address to send the lp tokens to
|
|
||||||
// TODO: Update this per documentation/Shorthand
|
|
||||||
SwapAndAddLiquidity(SeraiAddress),
|
|
||||||
// minimum out balance and out address
|
|
||||||
Swap(Balance, OutAddress),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub enum InInstruction {
|
|
||||||
Transfer(SeraiAddress),
|
|
||||||
Dex(DexCall),
|
|
||||||
GenesisLiquidity(SeraiAddress),
|
|
||||||
SwapToStakedSRI(SeraiAddress, NetworkId),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct RefundableInInstruction {
|
|
||||||
pub origin: Option<ExternalAddress>,
|
|
||||||
pub instruction: InInstruction,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct InInstructionWithBalance {
|
|
||||||
pub instruction: InInstruction,
|
|
||||||
pub balance: ExternalBalance,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, RuntimeDebug)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct Batch {
|
|
||||||
pub network: ExternalNetworkId,
|
|
||||||
pub id: u32,
|
|
||||||
pub external_network_block_hash: BlockHash,
|
|
||||||
pub instructions: Vec<InInstructionWithBalance>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, RuntimeDebug)]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub struct SignedBatch {
|
|
||||||
pub batch: Batch,
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "borsh",
|
|
||||||
borsh(
|
|
||||||
serialize_with = "serai_primitives::borsh_serialize_signature",
|
|
||||||
deserialize_with = "serai_primitives::borsh_deserialize_signature"
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
pub signature: Signature,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl Zeroize for SignedBatch {
|
|
||||||
fn zeroize(&mut self) {
|
|
||||||
self.batch.zeroize();
|
|
||||||
self.signature.as_mut().zeroize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make this an associated method?
|
|
||||||
/// The message for the batch signature.
|
|
||||||
pub fn batch_message(batch: &Batch) -> Vec<u8> {
|
|
||||||
[b"InInstructions-batch".as_ref(), &batch.encode()].concat()
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#[cfg(feature = "std")]
|
|
||||||
use zeroize::Zeroize;
|
|
||||||
|
|
||||||
#[cfg(feature = "borsh")]
|
|
||||||
use borsh::{BorshSerialize, BorshDeserialize};
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
use scale::{Encode, Decode, MaxEncodedLen};
|
|
||||||
use scale_info::TypeInfo;
|
|
||||||
|
|
||||||
use serai_primitives::{Amount, ExternalAddress, ExternalCoin, SeraiAddress};
|
|
||||||
|
|
||||||
use coins_primitives::OutInstruction;
|
|
||||||
|
|
||||||
use crate::RefundableInInstruction;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use crate::InInstruction;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
|
||||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub enum Shorthand {
|
|
||||||
Raw(RefundableInInstruction),
|
|
||||||
Swap {
|
|
||||||
origin: Option<ExternalAddress>,
|
|
||||||
coin: ExternalCoin,
|
|
||||||
minimum: Amount,
|
|
||||||
out: OutInstruction,
|
|
||||||
},
|
|
||||||
SwapAndAddLiquidity {
|
|
||||||
origin: Option<ExternalAddress>,
|
|
||||||
minimum: Amount,
|
|
||||||
gas: Amount,
|
|
||||||
address: SeraiAddress,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Shorthand {
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn transfer(origin: Option<ExternalAddress>, address: SeraiAddress) -> Self {
|
|
||||||
Self::Raw(RefundableInInstruction { origin, instruction: InInstruction::Transfer(address) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Shorthand> for RefundableInInstruction {
|
|
||||||
type Error = &'static str;
|
|
||||||
fn try_from(shorthand: Shorthand) -> Result<RefundableInInstruction, &'static str> {
|
|
||||||
Ok(match shorthand {
|
|
||||||
Shorthand::Raw(instruction) => instruction,
|
|
||||||
Shorthand::Swap { .. } => todo!(),
|
|
||||||
Shorthand::SwapAndAddLiquidity { .. } => todo!(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -381,7 +381,7 @@ pub mod pallet {
|
|||||||
|
|
||||||
// Explicitly provide a pre-dispatch which calls validate_unsigned
|
// Explicitly provide a pre-dispatch which calls validate_unsigned
|
||||||
fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
|
fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
|
||||||
Self::validate_unsigned(TransactionSource::InBlock, call).map(|_| ()).map_err(Into::into)
|
Self::validate_unsigned(TransactionSource::InBlock, call).map(|_| ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user