From 9d07ad9f3fc493e8e9ac917538bd0c160c187fde Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 10:32:48 +0800 Subject: [PATCH 01/11] add drand precompile --- Cargo.lock | 1 + contract-tests/src/contracts/drand.ts | 37 ++++++++ contract-tests/test/drand.precompile.test.ts | 97 ++++++++++++++++++++ pallets/admin-utils/src/lib.rs | 2 + precompiles/Cargo.toml | 2 + precompiles/src/drand.rs | 57 ++++++++++++ precompiles/src/lib.rs | 11 ++- 7 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 contract-tests/src/contracts/drand.ts create mode 100644 contract-tests/test/drand.precompile.test.ts create mode 100644 precompiles/src/drand.rs diff --git a/Cargo.lock b/Cargo.lock index 71fe31e5f4..c338d6a213 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18330,6 +18330,7 @@ dependencies = [ "pallet-admin-utils", "pallet-balances", "pallet-crowdloan", + "pallet-drand", "pallet-evm", "pallet-evm-precompile-bn128", "pallet-evm-precompile-dispatch", diff --git a/contract-tests/src/contracts/drand.ts b/contract-tests/src/contracts/drand.ts new file mode 100644 index 0000000000..32a0c5c9c0 --- /dev/null +++ b/contract-tests/src/contracts/drand.ts @@ -0,0 +1,37 @@ +// Drand precompile address: 0x80e = 2062 +export const IDRAND_ADDRESS = "0x000000000000000000000000000000000000080e"; + +export const IDrandABI = [ + { + inputs: [ + { + internalType: "uint64", + name: "round", + type: "uint64", + }, + ], + name: "getRandomness", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getLastStoredRound", + outputs: [ + { + internalType: "uint64", + name: "", + type: "uint64", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/contract-tests/test/drand.precompile.test.ts b/contract-tests/test/drand.precompile.test.ts new file mode 100644 index 0000000000..c6c40716d7 --- /dev/null +++ b/contract-tests/test/drand.precompile.test.ts @@ -0,0 +1,97 @@ +import * as assert from "assert"; + +import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"; +import { getPublicClient } from "../src/utils"; +import { ETH_LOCAL_URL } from "../src/config"; +import { devnet } from "@polkadot-api/descriptors"; +import { PublicClient } from "viem"; +import { TypedApi } from "polkadot-api"; +import { toViemAddress, convertPublicKeyToSs58 } from "../src/address-utils"; +import { IDrandABI, IDRAND_ADDRESS } from "../src/contracts/drand"; +import { forceSetBalanceToSs58Address, addNewSubnetwork, startCall } from "../src/subtensor"; + +describe("Test Drand Precompile", () => { + const hotkey = getRandomSubstrateKeypair(); + const coldkey = getRandomSubstrateKeypair(); + let publicClient: PublicClient; + let api: TypedApi; + + before(async () => { + publicClient = await getPublicClient(ETH_LOCAL_URL); + api = await getDevnetApi(); + + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)); + await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)); + + const netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + }); + + describe("Drand Randomness Functions", () => { + it("getLastStoredRound returns a value", async () => { + const lastRound = await publicClient.readContract({ + abi: IDrandABI, + address: toViemAddress(IDRAND_ADDRESS), + functionName: "getLastStoredRound", + args: [], + }); + + assert.ok(lastRound !== undefined, "getLastStoredRound should return a value"); + assert.strictEqual( + typeof lastRound, + "bigint", + "getLastStoredRound should return a bigint" + ); + assert.ok(lastRound >= BigInt(0), "Last stored round should be non-negative"); + }); + + it("getRandomness returns bytes32 for a round", async () => { + const lastRound = await publicClient.readContract({ + abi: IDrandABI, + address: toViemAddress(IDRAND_ADDRESS), + functionName: "getLastStoredRound", + args: [], + }); + + const roundToQuery = lastRound > BigInt(0) ? lastRound : BigInt(1); + + const randomness = await publicClient.readContract({ + abi: IDrandABI, + address: toViemAddress(IDRAND_ADDRESS), + functionName: "getRandomness", + args: [roundToQuery], + }); + + assert.ok(randomness !== undefined, "getRandomness should return a value"); + assert.strictEqual( + typeof randomness, + "string", + "getRandomness should return a hex string (bytes32)" + ); + assert.strictEqual( + randomness.length, + 66, + "bytes32 should be 0x + 64 hex chars" + ); + }); + + it("getRandomness for non-existent round returns zero bytes", async () => { + // Use a very high round number that will not have a stored pulse + const nonExistentRound = BigInt(999999999); + const randomness = await publicClient.readContract({ + abi: IDrandABI, + address: toViemAddress(IDRAND_ADDRESS), + functionName: "getRandomness", + args: [nonExistentRound], + }); + + assert.ok(randomness !== undefined, "getRandomness should return a value"); + const zeroBytes32 = "0x" + "0".repeat(64); + assert.strictEqual( + randomness.toLowerCase(), + zeroBytes32, + "getRandomness for non-existent round should return zero bytes32" + ); + }); + }); +}); diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 654f7207b8..81458735dc 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -147,6 +147,8 @@ pub mod pallet { AddressMapping, /// Voting power precompile VotingPower, + /// Drand randomness precompile + Drand, } #[pallet::type_value] diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml index be1824cf91..8bf412c503 100644 --- a/precompiles/Cargo.toml +++ b/precompiles/Cargo.toml @@ -39,6 +39,7 @@ pallet-admin-utils.workspace = true subtensor-swap-interface.workspace = true pallet-crowdloan.workspace = true pallet-shield.workspace = true +pallet-drand.workspace = true [lints] workspace = true @@ -65,6 +66,7 @@ std = [ "pallet-subtensor-swap/std", "pallet-subtensor/std", "pallet-shield/std", + "pallet-drand/std", "precompile-utils/std", "scale-info/std", "sp-core/std", diff --git a/precompiles/src/drand.rs b/precompiles/src/drand.rs new file mode 100644 index 0000000000..3a1449afba --- /dev/null +++ b/precompiles/src/drand.rs @@ -0,0 +1,57 @@ +use core::marker::PhantomData; + +use fp_evm::PrecompileHandle; +use precompile_utils::EvmResult; +use sp_core::H256; + +use crate::PrecompileExt; + +/// Drand precompile for smart contract access to Drand beacon randomness. +/// +/// This precompile allows smart contracts to read verifiable randomness from the +/// Drand Quicknet beacon that is bridged on-chain by the Drand pallet. +pub struct DrandPrecompile(PhantomData); + +impl PrecompileExt for DrandPrecompile +where + R: frame_system::Config + pallet_drand::Config, + R::AccountId: From<[u8; 32]>, +{ + const INDEX: u64 = 2062; +} + +#[precompile_utils::precompile] +impl DrandPrecompile +where + R: frame_system::Config + pallet_drand::Config, + R::AccountId: From<[u8; 32]>, +{ + /// Get the 32-byte randomness for a specific Drand round. + /// + /// Returns the SHA256 hash of the BLS signature for the given round. + /// Returns 32 zero bytes if no pulse exists for the round. + /// + /// # Arguments + /// * `round` - The Drand round number (u64) + /// + /// # Returns + /// * `bytes32` - The 32-byte randomness, or zeros if round not stored + #[precompile::public("getRandomness(uint64)")] + #[precompile::view] + fn get_randomness(_: &mut impl PrecompileHandle, round: u64) -> EvmResult { + let randomness = pallet_drand::Pallet::::random_at(round); + Ok(H256::from(randomness)) + } + + /// Get the last Drand round that has been stored on-chain. + /// + /// Returns 0 if no pulses have been stored yet. + /// + /// # Returns + /// * `uint64` - The last stored round number + #[precompile::public("getLastStoredRound()")] + #[precompile::view] + fn get_last_stored_round(_: &mut impl PrecompileHandle) -> EvmResult { + Ok(pallet_drand::LastStoredRound::::get()) + } +} diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index a824ac39d4..2a4c4e42f3 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -31,6 +31,7 @@ pub use address_mapping::AddressMappingPrecompile; pub use alpha::AlphaPrecompile; pub use balance_transfer::BalanceTransferPrecompile; pub use crowdloan::CrowdloanPrecompile; +pub use drand::DrandPrecompile; pub use ed25519::Ed25519Verify; pub use extensions::PrecompileExt; pub use leasing::LeasingPrecompile; @@ -48,6 +49,7 @@ mod address_mapping; mod alpha; mod balance_transfer; mod crowdloan; +mod drand; mod ed25519; mod extensions; mod leasing; @@ -75,6 +77,7 @@ where + pallet_crowdloan::Config + pallet_shield::Config + pallet_subtensor_proxy::Config + + pallet_drand::Config + Send + Sync + scale_info::TypeInfo, @@ -112,6 +115,7 @@ where + pallet_crowdloan::Config + pallet_shield::Config + pallet_subtensor_proxy::Config + + pallet_drand::Config + Send + Sync + scale_info::TypeInfo, @@ -136,7 +140,7 @@ where Self(Default::default()) } - pub fn used_addresses() -> [H160; 27] { + pub fn used_addresses() -> [H160; 28] { [ hash(1), hash(2), @@ -165,6 +169,7 @@ where hash(VotingPowerPrecompile::::INDEX), hash(ProxyPrecompile::::INDEX), hash(AddressMappingPrecompile::::INDEX), + hash(DrandPrecompile::::INDEX), ] } } @@ -180,6 +185,7 @@ where + pallet_crowdloan::Config + pallet_shield::Config + pallet_subtensor_proxy::Config + + pallet_drand::Config + Send + Sync + scale_info::TypeInfo, @@ -273,6 +279,9 @@ where PrecompileEnum::AddressMapping, ) } + a if a == hash(DrandPrecompile::::INDEX) => { + DrandPrecompile::::try_execute::(handle, PrecompileEnum::Drand) + } _ => None, } } From 961b6f68d626f294de5caebad0d6eb1245353cbe Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 15:52:43 +0800 Subject: [PATCH 02/11] add unit test --- contract-tests/test/drand.precompile.test.ts | 19 ++--- runtime/tests/precompiles.rs | 75 +++++++++++++++++++- 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/contract-tests/test/drand.precompile.test.ts b/contract-tests/test/drand.precompile.test.ts index c6c40716d7..14e70ec6a2 100644 --- a/contract-tests/test/drand.precompile.test.ts +++ b/contract-tests/test/drand.precompile.test.ts @@ -11,20 +11,12 @@ import { IDrandABI, IDRAND_ADDRESS } from "../src/contracts/drand"; import { forceSetBalanceToSs58Address, addNewSubnetwork, startCall } from "../src/subtensor"; describe("Test Drand Precompile", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); let publicClient: PublicClient; let api: TypedApi; before(async () => { publicClient = await getPublicClient(ETH_LOCAL_URL); api = await getDevnetApi(); - - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)); - - const netuid = await addNewSubnetwork(api, hotkey, coldkey); - await startCall(api, netuid, coldkey); }); describe("Drand Randomness Functions", () => { @@ -36,13 +28,15 @@ describe("Test Drand Precompile", () => { args: [], }); + const lastRoundFromApi = await api.query.Drand.LastStoredRound.getValue(); + assert.ok(lastRound !== undefined, "getLastStoredRound should return a value"); assert.strictEqual( typeof lastRound, "bigint", "getLastStoredRound should return a bigint" ); - assert.ok(lastRound >= BigInt(0), "Last stored round should be non-negative"); + assert.ok(lastRound == lastRoundFromApi, "Last stored round should match the value from the API"); }); it("getRandomness returns bytes32 for a round", async () => { @@ -62,6 +56,8 @@ describe("Test Drand Precompile", () => { args: [roundToQuery], }); + const randomnessFromApi = await api.query.Drand.Pulses.getValue(roundToQuery); + assert.ok(randomness !== undefined, "getRandomness should return a value"); assert.strictEqual( typeof randomness, @@ -73,6 +69,11 @@ describe("Test Drand Precompile", () => { 66, "bytes32 should be 0x + 64 hex chars" ); + assert.strictEqual( + randomness, + randomnessFromApi, + "Randomness should match the value from the API" + ); }); it("getRandomness for non-existent round returns zero bytes", async () => { diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs index 5565aa8f7f..dda2a65fbd 100644 --- a/runtime/tests/precompiles.rs +++ b/runtime/tests/precompiles.rs @@ -11,7 +11,8 @@ use precompile_utils::testing::{MockHandle, PrecompileTesterExt}; use sp_core::{H160, H256, U256}; use sp_runtime::traits::Hash; use subtensor_precompiles::{ - AddressMappingPrecompile, BalanceTransferPrecompile, PrecompileExt, Precompiles, + AddressMappingPrecompile, BalanceTransferPrecompile, DrandPrecompile, PrecompileExt, + Precompiles, }; type AccountId = ::AccountId; @@ -217,3 +218,75 @@ mod balance_transfer { }); } } + +mod drand { + use super::*; + + fn get_last_stored_round_call_data() -> Vec { + let selector = sp_io::hashing::keccak_256(b"getLastStoredRound()"); + selector[..4].to_vec() + } + + fn get_randomness_call_data(round: u64) -> Vec { + let selector = sp_io::hashing::keccak_256(b"getRandomness(uint64)"); + let mut input = Vec::with_capacity(4 + 32); + input.extend_from_slice(&selector[..4]); + input.extend_from_slice(&[0u8; 24]); + input.extend_from_slice(&round.to_be_bytes()); + input + } + + #[test] + fn drand_precompile_get_last_stored_round_returns_value() { + new_test_ext().execute_with(|| { + let precompiles = Precompiles::::new(); + let caller = addr_from_index(1); + let precompile_addr = addr_from_index(DrandPrecompile::::INDEX); + let input = get_last_stored_round_call_data(); + + let result = + execute_precompile(&precompiles, precompile_addr, caller, input, U256::zero()); + let precompile_result = + result.expect("expected precompile call to be routed to drand precompile"); + let output = precompile_result + .expect("expected successful getLastStoredRound call") + .output; + + assert_eq!( + output.len(), + 32, + "getLastStoredRound should return 32 bytes (uint64 ABI)" + ); + let _round = u64::from_be_bytes(output[24..32].try_into().unwrap()); + }); + } + + #[test] + fn drand_precompile_get_randomness_returns_bytes32() { + new_test_ext().execute_with(|| { + let precompiles = Precompiles::::new(); + let caller = addr_from_index(1); + let precompile_addr = addr_from_index(DrandPrecompile::::INDEX); + let non_existent_round = 999_999_999u64; + let input = get_randomness_call_data(non_existent_round); + + let result = + execute_precompile(&precompiles, precompile_addr, caller, input, U256::zero()); + let precompile_result = + result.expect("expected precompile call to be routed to drand precompile"); + let output = precompile_result + .expect("expected successful getRandomness call") + .output; + + assert_eq!( + output.len(), + 32, + "getRandomness should return 32 bytes (bytes32)" + ); + assert!( + output.iter().all(|&b| b == 0), + "getRandomness for non-existent round should return zero bytes" + ); + }); + } +} From eff67f4cee0ea52af37b3ef3b00a9bf7bd88c8b8 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 15:52:53 +0800 Subject: [PATCH 03/11] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 460a84e049..249975f86c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -243,7 +243,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 380, + spec_version: 381, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 348ea261ee2c5ac5f305ba47c40cb7168cb17d06 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 15:56:06 +0800 Subject: [PATCH 04/11] fix lint --- runtime/tests/precompiles.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs index dda2a65fbd..d8d19f24df 100644 --- a/runtime/tests/precompiles.rs +++ b/runtime/tests/precompiles.rs @@ -257,7 +257,6 @@ mod drand { 32, "getLastStoredRound should return 32 bytes (uint64 ABI)" ); - let _round = u64::from_be_bytes(output[24..32].try_into().unwrap()); }); } From d11f6ae56fae0bdd6c1a28cf9b77359aab51d32b Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 16:15:46 +0800 Subject: [PATCH 05/11] remove unused dependencyies --- contract-tests/test/drand.precompile.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contract-tests/test/drand.precompile.test.ts b/contract-tests/test/drand.precompile.test.ts index 14e70ec6a2..d4f9351cf6 100644 --- a/contract-tests/test/drand.precompile.test.ts +++ b/contract-tests/test/drand.precompile.test.ts @@ -1,14 +1,13 @@ import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"; +import { getDevnetApi } from "../src/substrate"; import { getPublicClient } from "../src/utils"; import { ETH_LOCAL_URL } from "../src/config"; import { devnet } from "@polkadot-api/descriptors"; import { PublicClient } from "viem"; import { TypedApi } from "polkadot-api"; -import { toViemAddress, convertPublicKeyToSs58 } from "../src/address-utils"; +import { toViemAddress } from "../src/address-utils"; import { IDrandABI, IDRAND_ADDRESS } from "../src/contracts/drand"; -import { forceSetBalanceToSs58Address, addNewSubnetwork, startCall } from "../src/subtensor"; describe("Test Drand Precompile", () => { let publicClient: PublicClient; From fae671763644307f6cf7b68462c7da271fd42e72 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 17:05:49 +0800 Subject: [PATCH 06/11] update unit test with real value --- runtime/tests/precompiles.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs index d8d19f24df..d47256a00f 100644 --- a/runtime/tests/precompiles.rs +++ b/runtime/tests/precompiles.rs @@ -5,7 +5,9 @@ use core::iter::IntoIterator; use std::collections::BTreeSet; use fp_evm::{Context, ExitError, PrecompileFailure, PrecompileResult}; +use frame_support::BoundedVec; use node_subtensor_runtime::{BuildStorage, Runtime, RuntimeGenesisConfig, System}; +use pallet_drand::{LastStoredRound, Pulses, types::Pulse}; use pallet_evm::{AddressMapping, BalanceConverter, PrecompileSet}; use precompile_utils::testing::{MockHandle, PrecompileTesterExt}; use sp_core::{H160, H256, U256}; @@ -239,6 +241,8 @@ mod drand { #[test] fn drand_precompile_get_last_stored_round_returns_value() { new_test_ext().execute_with(|| { + let round = 1000; + LastStoredRound::::put(round); let precompiles = Precompiles::::new(); let caller = addr_from_index(1); let precompile_addr = addr_from_index(DrandPrecompile::::INDEX); @@ -257,17 +261,28 @@ mod drand { 32, "getLastStoredRound should return 32 bytes (uint64 ABI)" ); + let output_u64 = u64::from_be_bytes(output[24..32].try_into().unwrap()); + assert_eq!(output_u64, round); }); } #[test] fn drand_precompile_get_randomness_returns_bytes32() { new_test_ext().execute_with(|| { + let round = 1000; + let value = 1u8; + Pulses::::insert( + round, + Pulse { + round: 1000, + randomness: BoundedVec::truncate_from(vec![value; 32]), + signature: BoundedVec::truncate_from(vec![0u8; 96]), + }, + ); let precompiles = Precompiles::::new(); let caller = addr_from_index(1); let precompile_addr = addr_from_index(DrandPrecompile::::INDEX); - let non_existent_round = 999_999_999u64; - let input = get_randomness_call_data(non_existent_round); + let input = get_randomness_call_data(round); let result = execute_precompile(&precompiles, precompile_addr, caller, input, U256::zero()); @@ -283,9 +298,20 @@ mod drand { "getRandomness should return 32 bytes (bytes32)" ); assert!( - output.iter().all(|&b| b == 0), - "getRandomness for non-existent round should return zero bytes" + output.iter().all(|&b| b == value), + "getRandomness for round 1000 should return the inserted randomness (32 bytes of 1s)" ); + + let non_existent_round = 999_999_999u64; + let input = get_randomness_call_data(non_existent_round); + let result = + execute_precompile(&precompiles, precompile_addr, caller, input, U256::zero()); + let precompile_result = + result.expect("expected precompile call to be routed to drand precompile"); + let output = precompile_result + .expect("expected successful getRandomness call") + .output; + assert!(output.iter().all(|&b| b == 0), "getRandomness for non-existent round should return 32 bytes of 0s"); }); } } From 36e40681f2a290d600a37ec7d450fa6d97c571c0 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 17:11:51 +0800 Subject: [PATCH 07/11] commit Cargo.lock --- runtime/tests/precompiles.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs index d47256a00f..cb9097f639 100644 --- a/runtime/tests/precompiles.rs +++ b/runtime/tests/precompiles.rs @@ -222,6 +222,7 @@ mod balance_transfer { } mod drand { + #![allow(clippy::slicing_panic)] use super::*; fn get_last_stored_round_call_data() -> Vec { @@ -261,7 +262,8 @@ mod drand { 32, "getLastStoredRound should return 32 bytes (uint64 ABI)" ); - let output_u64 = u64::from_be_bytes(output[24..32].try_into().unwrap()); + let output_u64 = + u64::from_be_bytes(output[24..32].try_info().expect("expected 8 bytes")); assert_eq!(output_u64, round); }); } From 4efcdd61f292cef3066c22b8600b6ad1c7e6ad91 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 17:13:07 +0800 Subject: [PATCH 08/11] commit Cargo.lock --- runtime/tests/precompiles.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs index cb9097f639..3c426e327d 100644 --- a/runtime/tests/precompiles.rs +++ b/runtime/tests/precompiles.rs @@ -263,7 +263,7 @@ mod drand { "getLastStoredRound should return 32 bytes (uint64 ABI)" ); let output_u64 = - u64::from_be_bytes(output[24..32].try_info().expect("expected 8 bytes")); + u64::from_be_bytes(output[24..32].try_into().expect("expected 8 bytes")); assert_eq!(output_u64, round); }); } From 471cfd4a037f46e16447e9d88bc535271d577b81 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 17:17:55 +0800 Subject: [PATCH 09/11] commit Cargo.lock --- runtime/tests/precompiles.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs index 3c426e327d..d47256a00f 100644 --- a/runtime/tests/precompiles.rs +++ b/runtime/tests/precompiles.rs @@ -222,7 +222,6 @@ mod balance_transfer { } mod drand { - #![allow(clippy::slicing_panic)] use super::*; fn get_last_stored_round_call_data() -> Vec { @@ -262,8 +261,7 @@ mod drand { 32, "getLastStoredRound should return 32 bytes (uint64 ABI)" ); - let output_u64 = - u64::from_be_bytes(output[24..32].try_into().expect("expected 8 bytes")); + let output_u64 = u64::from_be_bytes(output[24..32].try_into().unwrap()); assert_eq!(output_u64, round); }); } From 476745cdff5f059f65300b0d06dd4c25f3a1f6e8 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 17:20:00 +0800 Subject: [PATCH 10/11] commit Cargo.lock --- runtime/tests/precompiles.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/tests/precompiles.rs b/runtime/tests/precompiles.rs index d47256a00f..35b33db3c5 100644 --- a/runtime/tests/precompiles.rs +++ b/runtime/tests/precompiles.rs @@ -261,6 +261,7 @@ mod drand { 32, "getLastStoredRound should return 32 bytes (uint64 ABI)" ); + #[allow(clippy::indexing_slicing)] let output_u64 = u64::from_be_bytes(output[24..32].try_into().unwrap()); assert_eq!(output_u64, round); }); From 1d83e0e4eeff30bec0326742c6e42eba9d2cf7e3 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 19 Feb 2026 20:38:50 +0800 Subject: [PATCH 11/11] fix e2e test --- contract-tests/test/drand.precompile.test.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/contract-tests/test/drand.precompile.test.ts b/contract-tests/test/drand.precompile.test.ts index d4f9351cf6..76612012f7 100644 --- a/contract-tests/test/drand.precompile.test.ts +++ b/contract-tests/test/drand.precompile.test.ts @@ -27,7 +27,7 @@ describe("Test Drand Precompile", () => { args: [], }); - const lastRoundFromApi = await api.query.Drand.LastStoredRound.getValue(); + const lastRoundFromApi = await api.query.Drand.LastStoredRound.getValue({ at: "best" }); assert.ok(lastRound !== undefined, "getLastStoredRound should return a value"); assert.strictEqual( @@ -35,7 +35,7 @@ describe("Test Drand Precompile", () => { "bigint", "getLastStoredRound should return a bigint" ); - assert.ok(lastRound == lastRoundFromApi, "Last stored round should match the value from the API"); + assert.ok(lastRound === lastRoundFromApi, "Last stored round should match the value from the API"); }); it("getRandomness returns bytes32 for a round", async () => { @@ -46,16 +46,15 @@ describe("Test Drand Precompile", () => { args: [], }); - const roundToQuery = lastRound > BigInt(0) ? lastRound : BigInt(1); - const randomness = await publicClient.readContract({ abi: IDrandABI, address: toViemAddress(IDRAND_ADDRESS), functionName: "getRandomness", - args: [roundToQuery], + args: [lastRound], }); - const randomnessFromApi = await api.query.Drand.Pulses.getValue(roundToQuery); + const pulseFromApi = await api.query.Drand.Pulses.getValue(lastRound, { at: "best" }); + const randomnessFromApi = pulseFromApi?.randomness.asHex(); assert.ok(randomness !== undefined, "getRandomness should return a value"); assert.strictEqual( @@ -85,6 +84,8 @@ describe("Test Drand Precompile", () => { args: [nonExistentRound], }); + console.log("randomness", randomness); + assert.ok(randomness !== undefined, "getRandomness should return a value"); const zeroBytes32 = "0x" + "0".repeat(64); assert.strictEqual(