diff --git a/Cargo.lock b/Cargo.lock
index bdb913ce..caab2a5f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -462,8 +462,10 @@ dependencies = [
"clvmr",
"hex",
"hex-literal",
+ "k256",
"num-bigint",
"rstest",
+ "sha3",
"thiserror",
]
@@ -729,8 +731,7 @@ dependencies = [
[[package]]
name = "clvmr"
version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd48d84ed6eac4638131341402f30476c9f6c6970ed3ed6984bdf125c5a09538"
+source = "git+https://github.com/Chia-Network/clvm_rs?rev=2f413e72fcf1bcafa4a3117f2c2a0a3a0e7e1c6b#2f413e72fcf1bcafa4a3117f2c2a0a3a0e7e1c6b"
dependencies = [
"chia-bls 0.10.0",
"hex-literal",
@@ -741,6 +742,7 @@ dependencies = [
"num-traits",
"p256",
"sha2",
+ "sha3",
]
[[package]]
@@ -1171,6 +1173,12 @@ dependencies = [
"allocator-api2",
]
+[[package]]
+name = "hashbrown"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
+
[[package]]
name = "hermit-abi"
version = "0.3.9"
@@ -1245,12 +1253,12 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.4.0"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
+checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
- "hashbrown",
+ "hashbrown 0.15.1",
]
[[package]]
@@ -1300,6 +1308,15 @@ dependencies = [
"signature",
]
+[[package]]
+name = "keccak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
+dependencies = [
+ "cpufeatures",
+]
+
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -1378,7 +1395,7 @@ version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
dependencies = [
- "hashbrown",
+ "hashbrown 0.14.5",
]
[[package]]
@@ -2181,6 +2198,16 @@ dependencies = [
"digest",
]
+[[package]]
+name = "sha3"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
+dependencies = [
+ "digest",
+ "keccak",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
diff --git a/Cargo.toml b/Cargo.toml
index 75118d35..76863005 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -136,7 +136,12 @@ napi-derive = "2.12.2"
napi = { version = "2.12.2", default-features = false }
paste = "1.0.15"
bigdecimal = "0.4.6"
+k256 = "0.13.4"
+sha3 = "0.10.8"
[profile.release]
lto = true
strip = "symbols"
+
+[patch.crates-io]
+clvmr = { git = "https://github.com/Chia-Network/clvm_rs", rev = "2f413e72fcf1bcafa4a3117f2c2a0a3a0e7e1c6b" }
diff --git a/crates/chia-sdk-driver/Cargo.toml b/crates/chia-sdk-driver/Cargo.toml
index 3ed272bc..bd233bb1 100644
--- a/crates/chia-sdk-driver/Cargo.toml
+++ b/crates/chia-sdk-driver/Cargo.toml
@@ -33,6 +33,8 @@ hex-literal = { workspace = true }
num-bigint = { workspace = true}
hex = { workspace = true }
bigdecimal = { workspace = true }
+k256 = { workspace = true }
+sha3 = { workspace = true }
[dev-dependencies]
chia-sdk-test = { workspace = true }
diff --git a/crates/chia-sdk-driver/src/layers.rs b/crates/chia-sdk-driver/src/layers.rs
index 12612475..86e321f1 100644
--- a/crates/chia-sdk-driver/src/layers.rs
+++ b/crates/chia-sdk-driver/src/layers.rs
@@ -2,8 +2,10 @@ mod cat_layer;
mod did_layer;
mod nft_ownership_layer;
mod nft_state_layer;
+mod p2_controller_puzzle_layer;
mod p2_delegated_conditions_layer;
mod p2_delegated_singleton_layer;
+mod p2_eip712_message_layer;
mod p2_one_of_many;
mod p2_singleton;
mod royalty_transfer_layer;
@@ -15,8 +17,10 @@ pub use cat_layer::*;
pub use did_layer::*;
pub use nft_ownership_layer::*;
pub use nft_state_layer::*;
+pub use p2_controller_puzzle_layer::*;
pub use p2_delegated_conditions_layer::*;
pub use p2_delegated_singleton_layer::*;
+pub use p2_eip712_message_layer::*;
pub use p2_one_of_many::*;
pub use p2_singleton::*;
pub use royalty_transfer_layer::*;
diff --git a/crates/chia-sdk-driver/src/layers/p2_controller_puzzle_layer.rs b/crates/chia-sdk-driver/src/layers/p2_controller_puzzle_layer.rs
new file mode 100644
index 00000000..14e9538d
--- /dev/null
+++ b/crates/chia-sdk-driver/src/layers/p2_controller_puzzle_layer.rs
@@ -0,0 +1,175 @@
+use chia_protocol::Bytes32;
+use clvm_traits::{FromClvm, ToClvm};
+use clvm_utils::{CurriedProgram, TreeHash};
+use clvmr::{Allocator, NodePtr};
+use hex_literal::hex;
+
+use crate::{DriverError, Layer, Puzzle, Spend, SpendContext};
+
+// https://github.com/Yakuhito/hermes/blob/master/clsp/p2_controller_puzzle.clsp
+pub const P2_CONTROLLER_PUZZLE_PUZZLE: [u8; 151] = hex!("ff02ffff01ff04ffff04ff04ffff04ffff0117ffff04ffff02ff06ffff04ff02ffff04ff0bff80808080ffff04ff05ff8080808080ffff02ff0bff178080ffff04ffff01ff43ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080");
+pub const P2_CONTROLLER_PUZZLE_PUZZLE_HASH: TreeHash = TreeHash::new(hex!(
+ "
+ d5415713619e318bfa7820e06e2b163beef32d82294a5a7fcf9c3c69b0949c88
+ "
+));
+
+/// The p2 controller puzzle [`Layer`] allows a coin to be 'owned' by another puzzle,
+/// which sends a message containing a delegated puzzle for the controlled coin to run.
+///
+/// This is useful since it enables wallets to only ask for one signature instead of
+/// one signature/coin when using standard-like puzzles. Specifically, this puzzle
+/// was created to avoid having to sign multiple EIP-712 messages when using the
+/// p2 EIP-712 message puzzle.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct P2ControllerPuzzleLayer {
+ pub controller_puzzle_hash: Bytes32,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
+#[clvm(curry)]
+pub struct P2ControllerPuzzleArgs {
+ pub controller_puzzle_hash: Bytes32,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
+#[clvm(solution)]
+pub struct P2ControllerPuzzleSolution
{
+ pub delegated_puzzle: P,
+ pub delegated_solution: S,
+}
+
+impl P2ControllerPuzzleLayer {
+ pub fn new(controller_puzzle_hash: Bytes32) -> Self {
+ Self {
+ controller_puzzle_hash,
+ }
+ }
+
+ pub fn spend(
+ &self,
+ ctx: &mut SpendContext,
+ delegated_spend: Spend,
+ ) -> Result {
+ self.construct_spend(
+ ctx,
+ P2ControllerPuzzleSolution {
+ delegated_puzzle: delegated_spend.puzzle,
+ delegated_solution: delegated_spend.solution,
+ },
+ )
+ }
+}
+
+impl Layer for P2ControllerPuzzleLayer {
+ type Solution = P2ControllerPuzzleSolution;
+
+ fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result {
+ let curried = CurriedProgram {
+ program: ctx.p2_controller_puzzle_puzzle()?,
+ args: P2ControllerPuzzleArgs {
+ controller_puzzle_hash: self.controller_puzzle_hash,
+ },
+ };
+ ctx.alloc(&curried)
+ }
+
+ fn construct_solution(
+ &self,
+ ctx: &mut SpendContext,
+ solution: Self::Solution,
+ ) -> Result {
+ ctx.alloc(&solution)
+ }
+
+ fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result