From 506471db24817a94c26cd093e40b96d710bec5f2 Mon Sep 17 00:00:00 2001 From: Steve Degosserie <723552+stiiifff@users.noreply.github.com> Date: Mon, 2 Feb 2026 15:42:14 +0100 Subject: [PATCH] feat(test): expand Moonwall test coverage with balance and precompile tests (#414) ## Summary - Import and adapt balance tests from Moonbeam's test suite for DataHaven runtime compatibility - Add comprehensive precompile tests covering ERC20, modexp, proxy, identity, and cryptographic precompiles - Add helper utilities for precompile testing including address constants and contract call wrappers ## Changes ### Balance Tests - `test-balance-existential.ts` - Existential deposit behavior - `test-balance-extrinsics.ts` - Balance extrinsics (transfer, force_transfer) - `test-balance-genesis.ts` - Genesis balance verification - `test-balance-transfer.ts` - Various transfer scenarios ### Precompile Tests - **ERC20**: Native token interface tests including overflow handling - **Modexp**: Comprehensive modular exponentiation tests with extensive test vectors - **Proxy**: Proxy account management and proxy call tests - **Identity**: Full identity precompile test coverage (14 test files) - **Cryptographic**: blake2, bn128add, bn128mul, bn128pairing, ecrecover, ripemd160, sha3fips - **Preimage**: Preimage noting and unnoting tests ### Helper Utilities - `precompile-addresses.ts` - Precompile address constants - `precompile-contract-calls.ts` - Typed contract call helpers (Preimage class) - `modexp.ts` - Modexp test utilities ### Runtime Fixes - Fix Ethan's address in genesis presets to match Moonwall util constants --------- Co-authored-by: Claude Opus 4.5 Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com> Co-authored-by: Ahmad Kaouk --- biome.json | 1 + .../mainnet/src/genesis_config_presets.rs | 2 +- .../stagenet/src/genesis_config_presets.rs | 2 +- .../testnet/src/genesis_config_presets.rs | 2 +- test/moonwall/contracts/src/ECContracts.sol | 51 ++ test/moonwall/contracts/src/HasherChecker.sol | 226 +++++ test/moonwall/helpers/constants.ts | 37 +- test/moonwall/helpers/index.ts | 3 + test/moonwall/helpers/modexp.ts | 97 +++ test/moonwall/helpers/precompile-addresses.ts | 17 + .../helpers/precompile-contract-calls.ts | 136 +++ .../balance/test-balance-existential.ts | 93 ++ .../balance/test-balance-extrinsics.ts | 87 ++ .../stagenet/balance/test-balance-genesis.ts | 38 + .../stagenet/balance/test-balance-transfer.ts | 242 +++++ .../precompile/test-precompile-blake2.ts | 37 + .../precompile/test-precompile-bn128add.ts | 39 + .../precompile/test-precompile-bn128mul.ts | 39 + .../test-precompile-bn128pairing.ts | 39 + .../precompile/test-precompile-ecrecover.ts | 81 ++ .../test-precompile-erc20-overflow.ts | 31 + .../precompile/test-precompile-erc20.ts | 291 +++++++ .../precompile/test-precompile-identity.ts | 116 +++ .../precompile/test-precompile-identity10.ts | 61 ++ .../precompile/test-precompile-identity11.ts | 95 ++ .../precompile/test-precompile-identity12.ts | 87 ++ .../precompile/test-precompile-identity13.ts | 76 ++ .../precompile/test-precompile-identity14.ts | 73 ++ .../precompile/test-precompile-identity2.ts | 102 +++ .../precompile/test-precompile-identity3.ts | 67 ++ .../precompile/test-precompile-identity4.ts | 82 ++ .../precompile/test-precompile-identity5.ts | 82 ++ .../precompile/test-precompile-identity6.ts | 98 +++ .../precompile/test-precompile-identity7.ts | 82 ++ .../precompile/test-precompile-identity8.ts | 61 ++ .../precompile/test-precompile-identity9.ts | 73 ++ .../precompile/test-precompile-modexp.ts | 824 ++++++++++++++++++ .../precompile/test-precompile-preimage.ts | 106 +++ .../precompile/test-precompile-proxy.ts | 544 ++++++++++++ .../precompile/test-precompile-ripemd160.ts | 50 ++ .../precompile/test-precompile-sha3fips.ts | 30 + 41 files changed, 4291 insertions(+), 9 deletions(-) create mode 100644 test/moonwall/contracts/src/ECContracts.sol create mode 100644 test/moonwall/contracts/src/HasherChecker.sol create mode 100644 test/moonwall/helpers/modexp.ts create mode 100644 test/moonwall/helpers/precompile-addresses.ts create mode 100644 test/moonwall/helpers/precompile-contract-calls.ts create mode 100644 test/moonwall/suites/dev/stagenet/balance/test-balance-existential.ts create mode 100644 test/moonwall/suites/dev/stagenet/balance/test-balance-extrinsics.ts create mode 100644 test/moonwall/suites/dev/stagenet/balance/test-balance-genesis.ts create mode 100644 test/moonwall/suites/dev/stagenet/balance/test-balance-transfer.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-blake2.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128add.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128mul.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128pairing.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-ecrecover.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-erc20-overflow.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-erc20.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity10.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity11.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity12.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity13.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity14.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity2.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity3.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity4.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity5.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity6.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity7.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity8.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity9.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-modexp.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-preimage.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-proxy.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-ripemd160.ts create mode 100644 test/moonwall/suites/dev/stagenet/precompile/test-precompile-sha3fips.ts diff --git a/biome.json b/biome.json index 9c5ae7a4..aed14936 100644 --- a/biome.json +++ b/biome.json @@ -8,6 +8,7 @@ "**/*.yml", "**/*.md", "!node_modules/*", + "!**/moonwall/**/*", "!target/*", "!**/tmp/*", "!**/*.spec.json", diff --git a/operator/runtime/mainnet/src/genesis_config_presets.rs b/operator/runtime/mainnet/src/genesis_config_presets.rs index 15e9d2da..321df0a1 100644 --- a/operator/runtime/mainnet/src/genesis_config_presets.rs +++ b/operator/runtime/mainnet/src/genesis_config_presets.rs @@ -258,7 +258,7 @@ pub fn dorothy() -> AccountId { } pub fn ethan() -> AccountId { - AccountId::from(hex!("Ff64d3F6efE2317EE2807d2235B1ac2AA69d9E87")) + AccountId::from(hex!("Ff64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB")) } pub fn frank() -> AccountId { diff --git a/operator/runtime/stagenet/src/genesis_config_presets.rs b/operator/runtime/stagenet/src/genesis_config_presets.rs index 2fcbd983..0265f5a3 100644 --- a/operator/runtime/stagenet/src/genesis_config_presets.rs +++ b/operator/runtime/stagenet/src/genesis_config_presets.rs @@ -267,7 +267,7 @@ pub fn dorothy() -> AccountId { } pub fn ethan() -> AccountId { - AccountId::from(hex!("Ff64d3F6efE2317EE2807d2235B1ac2AA69d9E87")) + AccountId::from(hex!("Ff64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB")) } pub fn frank() -> AccountId { diff --git a/operator/runtime/testnet/src/genesis_config_presets.rs b/operator/runtime/testnet/src/genesis_config_presets.rs index d40eb073..e568f266 100644 --- a/operator/runtime/testnet/src/genesis_config_presets.rs +++ b/operator/runtime/testnet/src/genesis_config_presets.rs @@ -265,7 +265,7 @@ pub fn dorothy() -> AccountId { } pub fn ethan() -> AccountId { - AccountId::from(hex!("Ff64d3F6efE2317EE2807d2235B1ac2AA69d9E87")) + AccountId::from(hex!("Ff64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB")) } pub fn frank() -> AccountId { diff --git a/test/moonwall/contracts/src/ECContracts.sol b/test/moonwall/contracts/src/ECContracts.sol new file mode 100644 index 00000000..2faab0db --- /dev/null +++ b/test/moonwall/contracts/src/ECContracts.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.3; + +contract RecoveryChecker { + constructor() {} + + function checkRecovery( + bytes32 msgHash, + uint256 v, + bytes32 r, + bytes32 s + ) public view returns (address) { + (, bytes memory data) = address(0x01).staticcall( + abi.encode(msgHash, v, r, s) + ); + return abi.decode(data, (address)); + } +} + +contract PairingChecker { + bool public status = false; + + function callBn256Pairing(bytes memory input) + public + returns (bytes32 result) + { + uint256 len = input.length; + assembly { + let memPtr := mload(0x40) + let success := call( + gas(), + 0x08, + 0, + add(input, 0x20), + len, + memPtr, + 0x20 + ) + switch success + case 0 { + revert(0, 0) + } + default { + result := mload(memPtr) + } + } + status = + result == + 0x0000000000000000000000000000000000000000000000000000000000000001; + } +} diff --git a/test/moonwall/contracts/src/HasherChecker.sol b/test/moonwall/contracts/src/HasherChecker.sol new file mode 100644 index 00000000..b49eed63 --- /dev/null +++ b/test/moonwall/contracts/src/HasherChecker.sol @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +contract HasherChecker { + uint256 public lastResult; + + function ripemd160Check() public pure { + require( + ripemd160(bytes("Hello World!")) == + hex"8476ee4631b9b30ac2754b0ee0c47e161d3f724c" + ); + } + + function bn128AdditionCheck() public { + bool success; + uint256[4] memory input = [ + 0x2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703, + 0x301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915, + 0x18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9, + 0x063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266 + ]; + uint256[2] memory result; + + assembly { + // 0x06 id of the bn256Add precompile + // 0 number of ether to transfer + // 128 size of call parameters, i.e. 128 bytes total + // 64 size of return value, i.e. 64 bytes / 512 bit for a BN256 curve point + success := call(not(0), 0x06, 0, input, 128, result, 64) + } + require(success, "elliptic curve addition failed"); + require( + result[0] == + 0x2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7, + "failed" + ); + require( + result[1] == + 0x21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204, + "failed" + ); + } + + function bn128MultiplyCheck() public { + bool success; + uint256[3] memory input = [ + 0x1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3, + 0x1a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6, + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000 + ]; + uint256[2] memory result; + + assembly { + // 0x07 id of the bn256Mul precompile + // 0 number of ether to transfer + // 96 size of call parameters, i.e. 128 bytes total + // 64 size of return value, i.e. 64 bytes / 512 bit for a BN256 curve point + success := call(not(0), 0x07, 0, input, 96, result, 64) + } + require(success, "elliptic curve addition failed"); + require( + result[0] == + 0x1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3, + "failed" + ); + require( + result[1] == + 0x163511ddc1c3f25d396745388200081287b3fd1472d8339d5fecb2eae0830451, + "failed" + ); + } + + function bn128PairingCheck() public { + uint256[12] memory input = [ + 0x2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc02, + 0x03d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db84, + 0x1213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee, + 0x2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f, + 0x21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237, + 0x096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f, + 0x06967a1237ebfeca9aaae0d6d0bab8e28c198c5a339ef8a2407e31cdac516db9, + 0x22160fa257a5fd5b280642ff47b65eca77e626cb685c84fa6d3b6882a283ddd1, + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed, + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ]; + uint256[1] memory result; + bool success; + assembly { + // 0x08 id of the bn256CheckPairing precompile + // 0 number of ether to transfer + // 0 since we have an array of fixed length, our input starts in 0 + // 384 size of call parameters, i.e. 12*256 bits == 384 bytes + // 32 size of result (one 32 byte boolean!) + success := call(sub(gas(), 2000), 0x08, 0, input, 384, result, 32) + } + require(success, "elliptic curve pairing failed"); + require(result[0] == 1, "failed"); + } + + function modExpWrapper( + uint256 _b, + uint256 _e, + uint256 _m + ) public returns (uint256 result) { + assembly { + // Free memory pointer + let pointer := mload(0x40) + // Define length of base, exponent and modulus. 0x20 == 32 bytes + mstore(pointer, 0x20) + mstore(add(pointer, 0x20), 0x20) + mstore(add(pointer, 0x40), 0x20) + // Define variables base, exponent and modulus + mstore(add(pointer, 0x60), _b) + mstore(add(pointer, 0x80), _e) + + mstore(add(pointer, 0xa0), _m) + // Store the result + let value := mload(0xc0) + // Call the precompiled contract 0x05 = bigModExp + if iszero(call(not(0), 0x05, 0, pointer, 0xc0, value, 0x20)) { + revert(0, 0) + } + result := mload(value) + } + } + + function modExpVerify( + uint256 _base, + uint256 _exponent, + uint256 _modulus + ) public { + lastResult = modExpWrapper(_base, _exponent, _modulus); + } + + function getResult() public view returns (uint256) { + return lastResult; + } + + function modExpChecker() public { + require(modExpWrapper(3, 5, 7) == 5); + require(modExpWrapper(5, 7, 11) == 3); + } + + function blake2Wrapper( + uint32 rounds, + bytes32[2] memory h, + bytes32[4] memory m, + bytes8[2] memory t, + bool f + ) public view returns (bytes32[2] memory) { + bytes32[2] memory output; + + bytes memory args = abi.encodePacked( + rounds, + h[0], + h[1], + m[0], + m[1], + m[2], + m[3], + t[0], + t[1], + f + ); + + assembly { + if iszero( + staticcall(not(0), 0x09, add(args, 32), 0xd5, output, 0x40) + ) { + revert(0, 0) + } + } + + return output; + } + + function blake2Check() public { + uint32 rounds = 12; + + bytes32[2] memory h; + h[ + 0 + ] = hex"48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5"; + h[ + 1 + ] = hex"d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b"; + + bytes32[4] memory m; + m[ + 0 + ] = hex"6162630000000000000000000000000000000000000000000000000000000000"; + m[ + 1 + ] = hex"0000000000000000000000000000000000000000000000000000000000000000"; + m[ + 2 + ] = hex"0000000000000000000000000000000000000000000000000000000000000000"; + m[ + 3 + ] = hex"0000000000000000000000000000000000000000000000000000000000000000"; + + bytes8[2] memory t; + t[0] = hex"03000000"; + t[1] = hex"00000000"; + + bool f = true; + + // Expected output: + // ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1 + // 7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923 + + bytes32[2] memory result = blake2Wrapper(rounds, h, m, t, f); + require( + result[0] == + 0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1, + "failed" + ); + require( + result[1] == + 0x7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923, + "failed" + ); + } +} diff --git a/test/moonwall/helpers/constants.ts b/test/moonwall/helpers/constants.ts index 8b3123c2..d241ca69 100644 --- a/test/moonwall/helpers/constants.ts +++ b/test/moonwall/helpers/constants.ts @@ -4,11 +4,14 @@ */ import type { GenericContext } from "@moonwall/cli"; -import { - ALITH_GENESIS_FREE_BALANCE, - ALITH_GENESIS_LOCK_BALANCE, - ALITH_GENESIS_RESERVE_BALANCE -} from "@moonwall/util"; + +// DataHaven genesis balance constants +// From operator/runtime/stagenet/src/genesis_config_presets.rs: +// Each endowed account receives: 1u128 << 80 +// No locks or reserves are set at genesis +export const ALITH_GENESIS_FREE_BALANCE = 1n << 80n; // 1208925819614629174706176n +export const ALITH_GENESIS_LOCK_BALANCE = 0n; +export const ALITH_GENESIS_RESERVE_BALANCE = 0n; export const ALITH_GENESIS_TRANSFERABLE_COUNT = ALITH_GENESIS_FREE_BALANCE + ALITH_GENESIS_RESERVE_BALANCE - ALITH_GENESIS_LOCK_BALANCE; @@ -35,6 +38,27 @@ class RuntimeConstant { } } +// Currency units for DataHaven stagenet +// These match the runtime configuration in operator/runtime/stagenet/src/lib.rs +export const HAVE = 1_000_000_000_000_000_000n; // 10^18 +export const MICROHAVE = 1_000_000_000_000n; // 10^12 +export const SUPPLY_FACTOR = 1n; +export const STORAGE_BYTE_FEE = 100n * MICROHAVE * SUPPLY_FACTOR; // 100_000_000_000_000n + +/** + * Calculate deposit cost matching the runtime's deposit() function + * deposit(items, bytes) = items * HAVE * SUPPLY_FACTOR + bytes * STORAGE_BYTE_FEE + */ +export function deposit(items: number, bytes: number): bigint { + return BigInt(items) * HAVE * SUPPLY_FACTOR + BigInt(bytes) * STORAGE_BYTE_FEE; +} + +// Identity pallet deposit constants (stagenet) +// Calculated from: operator/runtime/stagenet/src/configs/mod.rs +export const IDENTITY_BASIC_DEPOSIT = deposit(1, 258); // 1_025_800_000_000_000_000n +export const IDENTITY_BYTE_DEPOSIT = deposit(0, 1); // 100_000_000_000_000n +export const IDENTITY_SUB_ACCOUNT_DEPOSIT = deposit(1, 53); // 1_005_300_000_000_000_000n + const DATAHAVEN_CONSTANTS = { BLOCK_WEIGHT_LIMIT: new RuntimeConstant({ 0: 2_000_000_000_000n @@ -57,7 +81,8 @@ const DATAHAVEN_CONSTANTS = { CALL_PERMIT: "0x000000000000000000000000000000000000080a" as const, PROXY: "0x000000000000000000000000000000000000080b" as const, ERC20_BALANCES: "0x0000000000000000000000000000000000000802" as const, - PRECOMPILE_REGISTRY: "0x0000000000000000000000000000000000000815" as const + PRECOMPILE_REGISTRY: "0x0000000000000000000000000000000000000815" as const, + IDENTITY: "0x0000000000000000000000000000000000000818" as const } } as const; diff --git a/test/moonwall/helpers/index.ts b/test/moonwall/helpers/index.ts index 76578e1e..ea1dffea 100644 --- a/test/moonwall/helpers/index.ts +++ b/test/moonwall/helpers/index.ts @@ -8,6 +8,7 @@ export * from "./block"; export * from "./constants"; +export { PRECOMPILE_IDENTITY_ADDRESS, PRECOMPILE_PROXY_ADDRESS } from "./precompile-addresses"; export * from "./contracts"; // Export unique functions from eth-transactions that aren't in evm.ts export { extractRevertReason } from "./eth-transactions"; @@ -16,3 +17,5 @@ export * from "./expect"; export * from "./fees"; export * from "./parameters"; export * from "./transactions"; +export * from "./modexp"; +export * from "./precompile-contract-calls"; diff --git a/test/moonwall/helpers/modexp.ts b/test/moonwall/helpers/modexp.ts new file mode 100644 index 00000000..1842468b --- /dev/null +++ b/test/moonwall/helpers/modexp.ts @@ -0,0 +1,97 @@ +/** + * Test vectors for modular exponentiation precompile tests + * Adapted from Moonbeam test helpers + */ + +export const testVectors = { + "nagydani-1-square": { + base: "e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5", + exponent: "02", + modulus: + "fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + }, + "nagydani-1-qube": { + base: "e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5", + exponent: "03", + modulus: + "fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + }, + "nagydani-1-pow0x10001": { + base: "e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5", + exponent: "010001", + modulus: + "fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + }, + "nagydani-2-square": { + base: "cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51", + exponent: "02", + modulus: + "e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + }, + "nagydani-2-qube": { + base: "cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51", + exponent: "03", + modulus: + "e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + }, + "nagydani-2-pow0x10001": { + base: "cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51", + exponent: "010001", + modulus: + "e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + }, + "nagydani-3-square": { + base: "c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb", + exponent: "02", + modulus: + "d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + }, + "nagydani-3-qube": { + base: "c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb", + exponent: "03", + modulus: + "d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + }, + "nagydani-3-pow0x10001": { + base: "c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb", + exponent: "010001", + modulus: + "d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + }, + "nagydani-4-square": { + base: "db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81", + exponent: "02", + modulus: + "df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + }, + "nagydani-4-qube": { + base: "db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81", + exponent: "03", + modulus: + "df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + }, + "nagydani-4-pow0x10001": { + base: "db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81", + exponent: "010001", + modulus: + "df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + }, + "nagydani-5-square": { + base: "c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf", + exponent: "02", + modulus: + "e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + }, + "nagydani-5-qube": { + base: "c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf", + exponent: "03", + modulus: + "e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + }, + "nagydani-5-pow0x10001": { + base: "c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf", + exponent: "010001", + modulus: + "e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + }, +} as const; diff --git a/test/moonwall/helpers/precompile-addresses.ts b/test/moonwall/helpers/precompile-addresses.ts new file mode 100644 index 00000000..4eac619b --- /dev/null +++ b/test/moonwall/helpers/precompile-addresses.ts @@ -0,0 +1,17 @@ +/** + * Precompile addresses for DataHaven + * These addresses match Moonbeam's precompile address scheme + */ + +export const PRECOMPILE_NATIVE_ERC20_ADDRESS = "0x0000000000000000000000000000000000000802" as const; +export const PRECOMPILE_ERC20_BALANCES_ADDRESS = PRECOMPILE_NATIVE_ERC20_ADDRESS; // Alias +export const PRECOMPILE_BATCH_ADDRESS = "0x0000000000000000000000000000000000000808" as const; +export const PRECOMPILE_CALL_PERMIT_ADDRESS = "0x000000000000000000000000000000000000080a" as const; +export const PRECOMPILE_PROXY_ADDRESS = "0x000000000000000000000000000000000000080b" as const; +export const PRECOMPILE_TREASURY_COUNCIL_ADDRESS = "0x0000000000000000000000000000000000000810" as const; +export const PRECOMPILE_REFERENDA_ADDRESS = "0x0000000000000000000000000000000000000811" as const; +export const PRECOMPILE_CONVICTION_VOTING_ADDRESS = "0x0000000000000000000000000000000000000812" as const; +export const PRECOMPILE_PREIMAGE_ADDRESS = "0x0000000000000000000000000000000000000813" as const; +export const PRECOMPILE_TECHNICAL_COMMITTEE_ADDRESS = "0x0000000000000000000000000000000000000814" as const; +export const PRECOMPILE_REGISTRY_ADDRESS = "0x0000000000000000000000000000000000000815" as const; +export const PRECOMPILE_IDENTITY_ADDRESS = "0x0000000000000000000000000000000000000818" as const; diff --git a/test/moonwall/helpers/precompile-contract-calls.ts b/test/moonwall/helpers/precompile-contract-calls.ts new file mode 100644 index 00000000..4753f08e --- /dev/null +++ b/test/moonwall/helpers/precompile-contract-calls.ts @@ -0,0 +1,136 @@ +/** + * Precompile contract call helpers + * Adapted from Moonbeam test suite + */ + +import type { BlockCreation, DevModeContext, PrecompileCallOptions } from "@moonwall/cli"; +import type { KeyringPair } from "@moonwall/util"; + +class PrecompileContract { + precompileName: string; + context: DevModeContext; + privateKey?: `0x${string}`; + gas?: bigint | "estimate"; + rawTxOnly?: boolean; + signer?: KeyringPair; + expectEvents?: [any]; + + constructor(precompileName: string, context: DevModeContext) { + this.precompileName = precompileName; + this.context = context; + this.reset(); + } + + reset() { + this.privateKey = undefined; + this.gas = undefined; + this.rawTxOnly = true; + this.signer = undefined; + this.expectEvents = undefined; + return this; + } + + withPrivateKey(privateKey: `0x${string}`) { + this.privateKey = privateKey; + return this; + } + + withGas(gas: bigint | "estimate") { + this.gas = gas; + return this; + } + + withRawTxOnly(rawTxOnly: boolean) { + if (rawTxOnly === false) { + this.rawTxOnly = undefined; + } + return this; + } + + withSigner(signer: KeyringPair) { + this.signer = signer; + return this; + } + + withExpectEvents(expectEvents: [any]) { + this.expectEvents = expectEvents; + return this; + } + + callExtrinsic(functionName: string, args: any[]): PrecompileCall { + return this.callRpc(functionName, args, true); + } + + callQuery(functionName: string, args: any[]): PrecompileCall { + return this.callRpc(functionName, args, false); + } + + private callRpc(functionName: string, args: any[], isExtrinsic: boolean): PrecompileCall { + const params = { + precompileName: this.precompileName, + functionName, + args, + privateKey: this.privateKey, + rawTxOnly: this.rawTxOnly, + gas: this.gas, + }; + const blockCreationOptions = { + signer: this.signer, + expectEvents: this.expectEvents, + }; + if (!isExtrinsic) { + return new ReadPrecompileCall(params, this.context, blockCreationOptions); + } + return new WritePrecompileCall(params, this.context, blockCreationOptions); + } +} + +export class PrecompileCall { + params: PrecompileCallOptions; + context: DevModeContext; + blockCreationOptions: BlockCreation; + + constructor( + params: PrecompileCallOptions, + context: DevModeContext, + blockCreationOptions: BlockCreation + ) { + this.params = params; + this.context = context; + this.blockCreationOptions = blockCreationOptions; + } + + async tx(): Promise { + throw new Error("Not implemented"); + } + + async block() { + return await this.context.createBlock((await this.tx()) as any, this.blockCreationOptions); + } +} + +class ReadPrecompileCall extends PrecompileCall { + async tx(): Promise { + return await this.context.readPrecompile!(this.params); + } +} + +class WritePrecompileCall extends PrecompileCall { + async tx(): Promise { + return await this.context.writePrecompile!(this.params); + } +} + +export class Preimage extends PrecompileContract { + constructor(context: DevModeContext) { + super("Preimage", context); + } + + notePreimage(data: string): PrecompileCall { + return this.callExtrinsic("notePreimage", [data]); + } + + unnotePreimage(data: string): PrecompileCall { + return this.callExtrinsic("unnotePreimage", [data]); + } +} diff --git a/test/moonwall/suites/dev/stagenet/balance/test-balance-existential.ts b/test/moonwall/suites/dev/stagenet/balance/test-balance-existential.ts new file mode 100644 index 00000000..a64afb84 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/balance/test-balance-existential.ts @@ -0,0 +1,93 @@ +/** + * Existential deposit tests + * Adapted from Moonbeam test suite + */ + +import { expect, describeSuite, beforeEach, TransactionTypes } from "@moonwall/cli"; +import { ALITH_ADDRESS, baltathar, GLMR } from "@moonwall/util"; +import { createRawTransfer } from "@moonwall/util"; +import { Wallet } from "ethers"; +import { ConstantStore } from "../../../../helpers"; + +describeSuite({ + id: "D030203", + title: "Existential Deposit", + foundationMethods: "dev", + testCases: ({ context, log, it }) => { + let privateKey: `0x${string}`; + let randomWeb3Account: any; + let GENESIS_BASE_FEE: bigint; + + beforeEach(async function () { + const { specVersion } = await context.polkadotJs().consts.system.version; + GENESIS_BASE_FEE = ConstantStore(context).GENESIS_BASE_FEE.get(specVersion.toNumber()); + + randomWeb3Account = context.web3().eth.accounts.create(); + privateKey = randomWeb3Account.privateKey; + const { result } = await context.createBlock( + context.polkadotJs().tx.balances.transferAllowDeath(randomWeb3Account.address, 10n * GLMR) + ); + expect(result!.successful, result!.error?.name).to.be.true; + }); + + for (const txnType of TransactionTypes) { + it({ + id: `T0${TransactionTypes.indexOf(txnType) + 1}`, + title: `full ${txnType} transfer should not reap on 0 account balance`, + test: async function () { + const raw = await createRawTransfer( + context, + ALITH_ADDRESS, + 10n * GLMR - 21000n * GENESIS_BASE_FEE, + { + privateKey, + type: txnType, + gasPrice: GENESIS_BASE_FEE, + gas: 21000n, + maxFeePerGas: GENESIS_BASE_FEE, + } + ); + const { result } = await context.createBlock(raw); + + expect( + await context.viem().getTransactionCount({ address: randomWeb3Account.address }) + ).toBe(1); + expect(result!.successful, result!.error?.name).toBe(true); + expect(await context.viem().getBalance({ address: randomWeb3Account.address })).toBe(0n); + }, + }); + } + + it({ + id: "T04", + title: "should not reap on tiny balance", + test: async function () { + const signer = new Wallet(privateKey, context.ethers().provider); + await signer.sendTransaction({ + to: baltathar.address, + value: 10n * GLMR - 21000n * GENESIS_BASE_FEE - 1n, + gasPrice: GENESIS_BASE_FEE, + gasLimit: 21000n, + }); + + await context.createBlock(); + + expect(await context.viem().getBalance({ address: randomWeb3Account.address })).toBe(1n); + expect( + await context.viem().getTransactionCount({ address: randomWeb3Account.address }) + ).toBe(1); + }, + }); + + it({ + id: "T05", + title: "runtime constant should be set to zero", + test: async function () { + const existentialDeposit = context + .polkadotJs() + .consts.balances.existentialDeposit.toBigInt(); + expect(existentialDeposit).toBe(0n); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/balance/test-balance-extrinsics.ts b/test/moonwall/suites/dev/stagenet/balance/test-balance-extrinsics.ts new file mode 100644 index 00000000..7704c1fe --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/balance/test-balance-extrinsics.ts @@ -0,0 +1,87 @@ +/** + * Balance extrinsics tests + * Adapted from Moonbeam test suite + */ + +import { TransactionTypes, beforeAll, beforeEach, describeSuite, expect } from "@moonwall/cli"; +import { + ALITH_ADDRESS, + BALTATHAR_ADDRESS, + GLMR, + createRawTransfer, + mapExtrinsics, +} from "@moonwall/util"; +import type { PrivateKeyAccount } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; + +describeSuite({ + id: "D030204", + title: "Balance - Extrinsic Events", + foundationMethods: "dev", + testCases: ({ context, log, it }) => { + let randomAccount: PrivateKeyAccount; + + beforeAll(async function () { + // To create the treasury account + await context.createBlock(createRawTransfer(context, BALTATHAR_ADDRESS, 1337)); + }); + + beforeEach(async function () { + const privateKey = generatePrivateKey(); + randomAccount = privateKeyToAccount(privateKey); + }); + + for (const txnType of TransactionTypes) { + it({ + id: `T0${TransactionTypes.indexOf(txnType) + 1}`, + title: `should emit events for ${txnType} ethereum/transfers`, + test: async function () { + await context.createBlock( + createRawTransfer(context, randomAccount.address, 1n * GLMR, { + type: txnType, + gas: 500000n, + }) + ); + + const signedBlock = await context.polkadotJs().rpc.chain.getBlock(); + const allRecords = await context.polkadotJs().query.system.events(); + const txsWithEvents = mapExtrinsics(signedBlock.block.extrinsics, allRecords); + + const ethTx = txsWithEvents.find( + ({ extrinsic: { method } }) => method.section === "ethereum" + )!; + + // Check key events are present + const hasNewAccount = ethTx.events.some((e) => + context.polkadotJs().events.system.NewAccount.is(e) + ); + const hasEndowed = ethTx.events.some((e) => + context.polkadotJs().events.balances.Endowed.is(e) + ); + const hasTransfer = ethTx.events.some((e) => + context.polkadotJs().events.balances.Transfer.is(e) + ); + const hasExecuted = ethTx.events.some((e) => + context.polkadotJs().events.ethereum.Executed.is(e) + ); + const hasSuccess = ethTx.events.some((e) => + context.polkadotJs().events.system.ExtrinsicSuccess.is(e) + ); + + expect(hasNewAccount, "NewAccount event should be present").to.be.true; + expect(hasEndowed, "Endowed event should be present").to.be.true; + expect(hasTransfer, "Transfer event should be present").to.be.true; + expect(hasExecuted, "Executed event should be present").to.be.true; + expect(hasSuccess, "ExtrinsicSuccess event should be present").to.be.true; + + // Verify transfer event data + const transferEvent = ethTx.events.find((e) => + context.polkadotJs().events.balances.Transfer.is(e) + )!; + expect(transferEvent.data[0].toString()).to.eq(ALITH_ADDRESS); + expect(transferEvent.data[1].toString()).to.eq(randomAccount.address); + }, + }); + } + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/balance/test-balance-genesis.ts b/test/moonwall/suites/dev/stagenet/balance/test-balance-genesis.ts new file mode 100644 index 00000000..41762f3b --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/balance/test-balance-genesis.ts @@ -0,0 +1,38 @@ +import "@moonbeam-network/api-augment"; +import { expect, describeSuite } from "@moonwall/cli"; +import { ALITH_ADDRESS } from "@moonwall/util"; +import { + ALITH_GENESIS_FREE_BALANCE, + ALITH_GENESIS_RESERVE_BALANCE, + ALITH_GENESIS_TRANSFERABLE_BALANCE, +} from "../../../../helpers"; + +describeSuite({ + id: "D030201", + title: "Balance genesis", + foundationMethods: "dev", + testCases: ({ context, it }) => { + it({ + id: "T01", + title: "should be accessible through web3", + test: async function () { + expect(await context.viem().getBalance({ address: ALITH_ADDRESS })).toBe( + ALITH_GENESIS_TRANSFERABLE_BALANCE + ); + }, + }); + + it({ + id: "T02", + title: "should be accessible through polkadotJs", + test: async function () { + const genesisHash = await context.polkadotJs().rpc.chain.getBlockHash(0); + const account = await (await context.polkadotJs().at(genesisHash)).query.system.account( + ALITH_ADDRESS + ); + expect(account.data.free.toBigInt()).toBe(ALITH_GENESIS_FREE_BALANCE); + expect(account.data.reserved.toBigInt()).toBe(ALITH_GENESIS_RESERVE_BALANCE); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/balance/test-balance-transfer.ts b/test/moonwall/suites/dev/stagenet/balance/test-balance-transfer.ts new file mode 100644 index 00000000..9c49a53f --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/balance/test-balance-transfer.ts @@ -0,0 +1,242 @@ +import "@moonbeam-network/api-augment"; +import { beforeEach, describeSuite, expect } from "@moonwall/cli"; +import { + ALITH_ADDRESS, + CHARLETH_ADDRESS, + BALTATHAR_ADDRESS, + BALTATHAR_PRIVATE_KEY, + CHARLETH_PRIVATE_KEY, + FAITH_PRIVATE_KEY, + GLMR, + checkBalance, + createViemTransaction, + createRawTransfer, + generateKeyringPair, + sendRawTransaction, +} from "@moonwall/util"; +import { + ALITH_GENESIS_TRANSFERABLE_BALANCE, + ConstantStore, + verifyLatestBlockFees, +} from "../../../../helpers"; + +import { parseGwei } from "viem"; + +describeSuite({ + id: "D030202", + title: "Balance Transfers", + foundationMethods: "dev", + testCases: ({ context, log, it }) => { + let randomAddress: `0x${string}`; + let GENESIS_BASE_FEE; + + beforeEach(async function () { + const randomAccount = generateKeyringPair(); + randomAddress = randomAccount.address as `0x${string}`; + const { specVersion } = await context.polkadotJs().consts.system.version; + GENESIS_BASE_FEE = ConstantStore(context).GENESIS_BASE_FEE.get(specVersion.toNumber()); + }); + + it({ + id: "T01", + title: "should cost 21000 gas for a transfer", + test: async function () { + const estimatedGas = await context.viem().estimateGas({ + account: ALITH_ADDRESS, + value: 0n * GLMR, + to: randomAddress, + }); + expect(estimatedGas, "Estimated bal transfer incorrect").toBe(21000n); + + await context.createBlock(createRawTransfer(context, randomAddress, 0n)); + expect(await context.viem().getBalance({ address: ALITH_ADDRESS })).toBe( + ALITH_GENESIS_TRANSFERABLE_BALANCE - 21000n * GENESIS_BASE_FEE + ); + }, + }); + + it({ + id: "T02", + title: "unsent txns should be in pending", + test: async function () { + await context.createBlock(); + const rawTx = (await createRawTransfer(context, randomAddress, 512n, { + privateKey: CHARLETH_PRIVATE_KEY, + gasPrice: GENESIS_BASE_FEE, + gas: 21000n, + txnType: "legacy", + })) as `0x${string}`; + await sendRawTransaction(context, rawTx); + + expect( + await context.viem().getBalance({ address: randomAddress, blockTag: "pending" }) + ).toBe(512n); + }, + }); + + it({ + id: "T03", + title: "should decrease from account", + test: async function () { + const balanceBefore = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + const fees = 21000n * GENESIS_BASE_FEE; + await context.createBlock( + await createRawTransfer(context, randomAddress, 512n, { + gas: 21000n, + gasPrice: GENESIS_BASE_FEE, + txnType: "legacy", + privateKey: CHARLETH_PRIVATE_KEY, + }) + ); + const balanceAfter = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(balanceBefore - balanceAfter - fees).toBe(512n); + }, + }); + + it({ + id: "T04", + title: "should increase to account", + test: async function () { + const balanceBefore = await checkBalance(context, randomAddress); + + await context.createBlock( + await createRawTransfer(context, randomAddress, 512n, { + gas: 21000n, + gasPrice: GENESIS_BASE_FEE, + type: "legacy", + }) + ); + const balanceAfter = await checkBalance(context, randomAddress); + expect(balanceBefore).toBe(0n); + expect(balanceAfter).toBe(512n); + }, + }); + + it({ + id: "T05", + title: "should reflect balance identically on polkadot/web3", + test: async function () { + await context.createBlock( + await createRawTransfer(context, randomAddress, 512n, { + gas: 21000n, + gasPrice: GENESIS_BASE_FEE, + type: "legacy", + }) + ); + + const blockNumber = ( + await context.polkadotJs().rpc.chain.getBlock() + ).block.header.number.toBigInt(); + + const block1Hash = await context.polkadotJs().rpc.chain.getBlockHash(blockNumber); + const balance = await (await context.polkadotJs().at(block1Hash)).query.system.account( + ALITH_ADDRESS + ); + + expect(await context.viem().getBalance({ blockNumber, address: ALITH_ADDRESS })).to.equal( + balance.data.free.toBigInt() + + balance.data.reserved.toBigInt() - + balance.data.frozen.toBigInt() + ); + }, + }); + + it({ + id: "T06", + title: "should check latest block fees", + test: async function () { + await context.createBlock( + await createRawTransfer(context, randomAddress, 512n, { + gas: 21000n, + gasPrice: GENESIS_BASE_FEE, + type: "legacy", + }) + ); + + await verifyLatestBlockFees(context, BigInt(512)); + }, + }); + + // NOTE: Uses FAITH_PRIVATE_KEY (aka Frank in DataHaven) instead of GERALD_PRIVATE_KEY + // because Gerald is not endowed at genesis in DataHaven (unlike Moonbeam). + it({ + id: "T07", + title: "multiple transfer should be successful", + test: async function () { + const { result } = await context.createBlock([ + await createRawTransfer(context, randomAddress, 10n * GLMR, { + privateKey: FAITH_PRIVATE_KEY, + nonce: 0, + }), + await createRawTransfer(context, randomAddress, 10n * GLMR, { + privateKey: FAITH_PRIVATE_KEY, + nonce: 1, + }), + await createRawTransfer(context, randomAddress, 10n * GLMR, { + privateKey: FAITH_PRIVATE_KEY, + nonce: 2, + }), + await createRawTransfer(context, randomAddress, 10n * GLMR, { + privateKey: FAITH_PRIVATE_KEY, + nonce: 3, + }), + await createRawTransfer(context, randomAddress, 10n * GLMR, { + privateKey: FAITH_PRIVATE_KEY, + nonce: 4, + }), + ]); + + expect((result as any).filter((r: any) => r.successful)).to.be.length(5); + }, + }); + + it({ + id: "T08", + title: "should handle max_fee_per_gas", + test: async function () { + const balanceBefore = await checkBalance(context, CHARLETH_ADDRESS); + await context.createBlock( + await createRawTransfer(context, randomAddress, 1n * GLMR, { + gas: 21000n, + maxFeePerGas: GENESIS_BASE_FEE, + maxPriorityFeePerGas: parseGwei("0.2"), + gasPrice: GENESIS_BASE_FEE, + type: "eip1559", + privateKey: CHARLETH_PRIVATE_KEY, + }) + ); + const balanceAfter = await checkBalance(context, CHARLETH_ADDRESS); + const fee = 21000n * GENESIS_BASE_FEE; + + expect(balanceAfter + fee + 1n * GLMR).toBe(balanceBefore); + }, + }); + + it({ + id: "T09", + title: "should use partial max_priority_fee_per_gas", + test: async function () { + // With this configuration only half of the priority fee will be used, + // as the max_fee_per_gas is 2GWEI and the base fee is 1GWEI. + const accountData = (await context.polkadotJs().query.system.account(BALTATHAR_ADDRESS)) + .data; + const freeBal = accountData.free.toBigInt() - accountData.reserved.toBigInt(); + const maxFeePerGas = 10_000_000_000n * 2n; + await context.createBlock( + await createViemTransaction(context, { + privateKey: BALTATHAR_PRIVATE_KEY, + gas: 21000n, + to: randomAddress, + data: "0x", + maxFeePerGas, + maxPriorityFeePerGas: maxFeePerGas, + type: "eip1559", + }) + ); + const balanceAfter = await checkBalance(context, BALTATHAR_ADDRESS); + const fee = 21_000n * maxFeePerGas; + expect(freeBal - balanceAfter - fee).toBe(0n); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-blake2.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-blake2.ts new file mode 100644 index 00000000..c936fd84 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-blake2.ts @@ -0,0 +1,37 @@ +/** + * Blake2 precompile tests + * Adapted from Moonbeam test suite + */ + +import { deployCreateCompiledContract, describeSuite } from "@moonwall/cli"; +import { expectEVMResult } from "../../../../helpers"; + +import { createViemTransaction } from "@moonwall/util"; +import { encodeFunctionData } from "viem"; + +describeSuite({ + id: "D030101", + title: "Precompiles - blake2", + foundationMethods: "dev", + testCases: ({ context, log, it }) => { + it({ + id: "T01", + title: "should be accessible from a smart contract", + test: async function () { + const { abi } = await deployCreateCompiledContract(context, "HasherChecker"); + + // Execute the contract blake2 call + const { result } = await context.createBlock( + createViemTransaction(context, { + data: encodeFunctionData({ + abi, + functionName: "blake2Check", + }), + }) + ); + + expectEVMResult(result!.events, "Succeed"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128add.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128add.ts new file mode 100644 index 00000000..9e7c455a --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128add.ts @@ -0,0 +1,39 @@ +/** + * BN128 addition precompile tests + * Adapted from Moonbeam test suite + */ + +import { deployCreateCompiledContract, describeSuite } from "@moonwall/cli"; +import { createViemTransaction } from "@moonwall/util"; +import { encodeFunctionData } from "viem"; +import { expectEVMResult } from "../../../../helpers"; + +describeSuite({ + id: "D030102", + title: "Precompiles - bn128add", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should be accessible from a smart contract", + test: async function () { + const { abi, contractAddress } = await deployCreateCompiledContract( + context, + "HasherChecker" + ); + + const { result } = await context.createBlock( + createViemTransaction(context, { + to: contractAddress, + data: encodeFunctionData({ + abi, + functionName: "bn128AdditionCheck", + }), + }) + ); + + expectEVMResult(result!.events, "Succeed"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128mul.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128mul.ts new file mode 100644 index 00000000..ffd0144b --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128mul.ts @@ -0,0 +1,39 @@ +/** + * BN128 multiplication precompile tests + * Adapted from Moonbeam test suite + */ + +import { deployCreateCompiledContract, describeSuite } from "@moonwall/cli"; +import { createViemTransaction } from "@moonwall/util"; +import { encodeFunctionData } from "viem"; +import { expectEVMResult } from "../../../../helpers"; + +describeSuite({ + id: "D030103", + title: "Precompiles - bn128mul", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should be accessible from a smart contract", + test: async function () { + const { abi, contractAddress } = await deployCreateCompiledContract( + context, + "HasherChecker" + ); + + const { result } = await context.createBlock( + createViemTransaction(context, { + to: contractAddress, + data: encodeFunctionData({ + abi, + functionName: "bn128MultiplyCheck", + }), + }) + ); + + expectEVMResult(result!.events, "Succeed"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128pairing.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128pairing.ts new file mode 100644 index 00000000..dd65daad --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-bn128pairing.ts @@ -0,0 +1,39 @@ +/** + * BN128 pairing precompile tests + * Adapted from Moonbeam test suite + */ + +import { deployCreateCompiledContract, describeSuite } from "@moonwall/cli"; +import { createViemTransaction } from "@moonwall/util"; +import { encodeFunctionData } from "viem"; +import { expectEVMResult } from "../../../../helpers"; + +describeSuite({ + id: "D030104", + title: "Precompiles - bn128pairing", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + it({ + id: "T01", + title: "should be accessible from a smart contract", + test: async function () { + const { abi, contractAddress } = await deployCreateCompiledContract( + context, + "HasherChecker" + ); + + const { result } = await context.createBlock( + createViemTransaction(context, { + to: contractAddress, + data: encodeFunctionData({ + abi, + functionName: "bn128PairingCheck", + }), + }) + ); + + expectEVMResult(result!.events, "Succeed"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-ecrecover.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-ecrecover.ts new file mode 100644 index 00000000..5a7ef7c4 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-ecrecover.ts @@ -0,0 +1,81 @@ +/** + * Ecrecover precompile tests + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { ALITH_ADDRESS, ALITH_PRIVATE_KEY } from "@moonwall/util"; + +describeSuite({ + id: "D030107", + title: "Precompiles - ecrecover", + foundationMethods: "dev", + testCases: ({ context, log, it }) => { + let contractAddress: `0x${string}`; + + beforeAll(async function () { + const { contractAddress: deployedAddr } = await context.deployContract!("RecoveryChecker"); + contractAddress = deployedAddr; + }); + + it({ + id: "T01", + title: "returns a matching address", + test: async function () { + const msg = context.web3().utils.sha3("Hello World!"); + const sig = context.web3().eth.accounts.sign(msg!, ALITH_PRIVATE_KEY); + + const address = await context.readContract!({ + contractAddress, + contractName: "RecoveryChecker", + functionName: "checkRecovery", + args: [sig.messageHash, sig.v, sig.r, sig.s], + }); + + expect(address, "Recovered address doesn't match signer!").to.equals(ALITH_ADDRESS); + }, + }); + + it({ + id: "T02", + title: "returns different address on modified message", + test: async function () { + const msg = context.web3().utils.sha3("Hello World!"); + const sig = context.web3().eth.accounts.sign(msg!, ALITH_PRIVATE_KEY); + + const address = await context.readContract!({ + contractAddress, + contractName: "RecoveryChecker", + functionName: "checkRecovery", + args: [sig.messageHash.replace("1", "f"), sig.v, sig.r, sig.s], + }); + + expect(address, "Recovered address doesn't match signer!").to.equals( + "0x58188b9AE77F7C865b04B12F5D29bF4fbDcbd937" + ); + }, + }); + + it({ + id: "T03", + title: "returns empty on invalid V", + test: async function () { + const msg = context.web3().utils.sha3("Hello World!"); + const sig = context.web3().eth.accounts.sign(msg!, ALITH_PRIVATE_KEY); + const v = "0x565656ff5656ffffffffffff3d3d02000000000040003dffff565656560f0000"; + + await expect( + async () => + await context.readContract!({ + contractAddress, + contractName: "RecoveryChecker", + functionName: "checkRecovery", + args: [sig.messageHash, v, sig.r, sig.s], + web3Library: "ethers", + gas: 1_000_000n, + }) + ).rejects.toThrowError("revert"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-erc20-overflow.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-erc20-overflow.ts new file mode 100644 index 00000000..0409f157 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-erc20-overflow.ts @@ -0,0 +1,31 @@ +import "@moonbeam-network/api-augment"; +import { describeSuite, expect } from "@moonwall/cli"; +import { generateKeyringPair } from "@moonwall/util"; + +describeSuite({ + id: "D030402", + title: "Precompile ERC20 - Transfering through precompile", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + const randomAccount = generateKeyringPair(); + it({ + id: "T01", + title: "should not allow overflowing the value", + test: async function () { + await expect( + async () => + await context.writePrecompile!({ + precompileName: "Batch", + functionName: "batchAll", + args: [ + [randomAccount.address], + [`${(2n ** 128n + 5_000_000_000_000_000_000n).toString()}`], + [], + [], + ], + }) + ).rejects.toThrowError("evm error: OutOfFund"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-erc20.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-erc20.ts new file mode 100644 index 00000000..0d9a132d --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-erc20.ts @@ -0,0 +1,291 @@ +import "@moonbeam-network/api-augment"; +import { beforeEach, describeSuite, expect } from "@moonwall/cli"; +import { + ALITH_ADDRESS, + BALTATHAR_ADDRESS, + BALTATHAR_PRIVATE_KEY, + CHARLETH_ADDRESS, + PRECOMPILE_NATIVE_ERC20_ADDRESS, + baltathar, +} from "@moonwall/util"; +import { type PrivateKeyAccount, keccak256, pad, parseEther, toBytes, toHex } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { ALITH_GENESIS_TRANSFERABLE_BALANCE } from "../../../../helpers"; + +// const SELECTORS = { +// balanceOf: "70a08231", +// totalSupply: "18160ddd", +// approve: "095ea7b3", +// allowance: "dd62ed3e", +// transfer: "a9059cbb", +// transferFrom: "23b872dd", +// deposit: "d0e30db0", +// logApprove: "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", +// logTransfer: "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", +// }; + +// Error(string) +const ABI_REVERT_SELECTOR = "0x08c379a0"; + +describeSuite({ + id: "D030401", + title: "Precompiles - ERC20 Native", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let randomAccount: PrivateKeyAccount; + + beforeEach(async () => { + randomAccount = privateKeyToAccount(generatePrivateKey()); + }); + + it({ + id: "T01", + title: "allows to call getBalance", + test: async function () { + const balance = await context.readPrecompile!({ + precompileName: "NativeErc20", + functionName: "balanceOf", + args: [ALITH_ADDRESS], + }); + + const signedTx = context + .polkadotJs() + .tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 1000) + .signAsync(baltathar); + await context.createBlock(signedTx); + + const tx = context.polkadotJs().tx.balances.transferAllowDeath(CHARLETH_ADDRESS, 1000); + await context.createBlock(tx, { + signer: { privateKey: BALTATHAR_PRIVATE_KEY, type: "ethereum" }, + }); + + expect(balance).equals(ALITH_GENESIS_TRANSFERABLE_BALANCE); + }, + }); + + it({ + id: "T02", + title: "allows to call totalSupply", + test: async function () { + const totalSupply = await context.readPrecompile!({ + precompileName: "NativeErc20", + functionName: "totalSupply", + }); + + const totalIssuance = ( + await context.polkadotJs().query.balances.totalIssuance() + ).toBigInt(); + expect(totalSupply).toBe(totalIssuance); + }, + }); + + it({ + id: "T03", + title: "allows to approve transfers, and allowance matches", + test: async function () { + const allowanceBefore = (await context.readPrecompile!({ + precompileName: "NativeErc20", + functionName: "allowance", + args: [ALITH_ADDRESS, BALTATHAR_ADDRESS], + })) as bigint; + const amount = parseEther("10"); + + const rawTx = await context.writePrecompile!({ + precompileName: "NativeErc20", + functionName: "approve", + args: [BALTATHAR_ADDRESS, amount], + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + + const allowanceAfter = (await context.readPrecompile!({ + precompileName: "NativeErc20", + functionName: "allowance", + args: [ALITH_ADDRESS, BALTATHAR_ADDRESS], + })) as bigint; + + expect(allowanceAfter - allowanceBefore).equals(BigInt(amount)); + + const { status, logs } = await context + .viem() + .getTransactionReceipt({ hash: result?.hash as `0x${string}` }); + + expect(status).to.equal("success"); + expect(logs.length).to.eq(1); + expect(logs[0].topics[0]).toBe(keccak256(toBytes("Approval(address,address,uint256)"))); + expect(logs[0].topics[1]?.toLowerCase()).toBe( + pad(ALITH_ADDRESS.toLowerCase() as `0x${string}`) + ); + expect(logs[0].topics[2]?.toLowerCase()).toBe( + pad(BALTATHAR_ADDRESS.toLowerCase() as `0x${string}`) + ); + }, + }); + + it({ + id: "T04", + title: "allows to call transfer", + test: async function () { + expect( + await context.readPrecompile!({ + precompileName: "NativeErc20", + functionName: "balanceOf", + args: [randomAccount.address], + }) + ).equals(0n); + + const balanceBefore = await context.viem().getBalance({ address: BALTATHAR_ADDRESS }); + + const rawTx = await context.writePrecompile!({ + precompileName: "NativeErc20", + functionName: "transfer", + args: [randomAccount.address, parseEther("3")], + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + const { status, gasUsed } = await context + .viem() + .getTransactionReceipt({ hash: result?.hash as `0x${string}` }); + expect(status).to.equal("success"); + + const balanceAfter = await context.viem().getBalance({ address: BALTATHAR_ADDRESS }); + const block = await context.viem().getBlock(); + const fees = gasUsed * block.baseFeePerGas!; + expect(balanceAfter).toBeLessThanOrEqual(balanceBefore - parseEther("3") - fees); + expect(await context.viem().getBalance({ address: randomAccount.address })).to.equal( + parseEther("3") + ); + }, + }); + + it({ + id: "T05", + title: "allows to approve transfer and use transferFrom", + test: async function () { + const allowedAmount = parseEther("10"); + const transferAmount = parseEther("5"); + + await context.writePrecompile!({ + precompileName: "NativeErc20", + functionName: "approve", + args: [BALTATHAR_ADDRESS, allowedAmount], + }); + await context.createBlock(); + + const fromBalBefore = ( + await context.polkadotJs().query.system.account(ALITH_ADDRESS) + ).data.free.toBigInt(); + const toBalBefore = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + + const rawTx = await context.writePrecompile!({ + precompileName: "NativeErc20", + functionName: "transferFrom", + args: [ALITH_ADDRESS, CHARLETH_ADDRESS, transferAmount], + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + }); + + const { result } = await context.createBlock(rawTx); + const { logs, status } = await context + .viem() + .getTransactionReceipt({ hash: result?.hash as `0x${string}` }); + + const fromBalAfter = ( + await context.polkadotJs().query.system.account(ALITH_ADDRESS) + ).data.free.toBigInt(); + + const toBalAfter = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(logs.length).to.eq(1); + expect(logs[0].address).to.eq(PRECOMPILE_NATIVE_ERC20_ADDRESS); + expect(logs[0].data).to.eq(pad(toHex(transferAmount))); + expect(logs[0].topics.length).to.eq(3); + expect(logs[0].topics[0]).toBe(keccak256(toBytes("Transfer(address,address,uint256)"))); + expect(logs[0].topics[1]?.toLowerCase()).toBe( + pad(ALITH_ADDRESS.toLowerCase() as `0x${string}`) + ); + expect(logs[0].topics[2]?.toLowerCase()).toBe( + pad(CHARLETH_ADDRESS.toLowerCase() as `0x${string}`) + ); + expect(status).to.equal("success"); + expect(toBalAfter).toBe(toBalBefore + transferAmount); + expect(fromBalAfter).toBe(fromBalBefore - transferAmount); + const newAllowedAmount = allowedAmount - transferAmount; + expect( + await context.readPrecompile!({ + precompileName: "NativeErc20", + functionName: "allowance", + args: [ALITH_ADDRESS, BALTATHAR_ADDRESS], + }) + ).toBe(newAllowedAmount); + }, + }); + + it({ + id: "T06", + title: "refuses to transferFrom more than allowed", + test: async function () { + const allowedAmount = parseEther("10"); + const transferAmount = parseEther("15"); + + await context.writePrecompile!({ + precompileName: "NativeErc20", + functionName: "approve", + args: [BALTATHAR_ADDRESS, allowedAmount], + }); + await context.createBlock(); + + const fromBalBefore = ( + await context.polkadotJs().query.system.account(ALITH_ADDRESS) + ).data.free.toBigInt(); + const toBalBefore = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + + const rawTxn = await context.writePrecompile!({ + precompileName: "NativeErc20", + functionName: "transferFrom", + args: [ALITH_ADDRESS, CHARLETH_ADDRESS, transferAmount], + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + gas: 200_000n, + web3Library: "ethers", + }); + + const { result } = await context.createBlock(rawTxn); + expect(result?.successful).toBe(false); + + const fromBalAfter = ( + await context.polkadotJs().query.system.account(ALITH_ADDRESS) + ).data.free.toBigInt(); + + const toBalAfter = await context.viem().getBalance({ address: CHARLETH_ADDRESS }); + expect(toBalAfter).toBe(toBalBefore); + expect(fromBalAfter).toBe(fromBalBefore); + expect( + await context.readPrecompile!({ + precompileName: "NativeErc20", + functionName: "allowance", + args: [ALITH_ADDRESS, BALTATHAR_ADDRESS], + }) + ).toBe(allowedAmount); + }, + }); + }, +}); + +// describeDevMoonbeamAllEthTxTypes("Precompiles - ERC20 Native", (context) => { +// it("revert message is abi-encoded as a String(error) call", async function () { +// const request = await web3EthCall(context.web3, { +// to: PRECOMPILE_NATIVE_ERC20_ADDRESS, +// data: `0x${SELECTORS.deposit}`, +// }); +// expect(request as any).to.haveOwnProperty("error"); +// // Data +// let data = (request as any).error.data; +// expect(data.length).to.be.eq(266); +// expect(data.slice(0, 10)).to.be.eq(ABI_REVERT_SELECTOR); +// // Message +// expect((request as any).error.message).to.be.eq( +// "VM Exception while processing transaction: revert deposited amount must be non-zero" +// ); +// }); +// }); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity.ts new file mode 100644 index 00000000..d5921eae --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity.ts @@ -0,0 +1,116 @@ +/** + * Identity precompile tests - retrieve registrars and identity + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { alith, baltathar } from "@moonwall/util"; +import { toHex } from "viem"; +import { PRECOMPILE_IDENTITY_ADDRESS } from "../../../../helpers"; + +describeSuite({ + id: "D030301", + title: "Precompiles - Identity precompile", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.identity.addRegistrar(alith.address)) + ); + + await context.createBlock(context.polkadotJs().tx.identity.setFee(0, 100)); + await context.createBlock( + // With 0b111 we are indicating the pallet to use "Display", "Legal" and "Web" fields. + context + .polkadotJs() + .tx.identity.setFields(0, 0b111 as any) + ); + + await context.createBlock( + context + .polkadotJs() + .tx.identity.setIdentity({ + additional: [[{ raw: "discord" }, { raw: "my-discord" }]], + display: { raw: "display" }, + legal: { raw: "legal" }, + web: { raw: "web" }, + riot: { raw: "riot" }, + email: { raw: "email" }, + pgpFingerprint: new Array(20).fill(1), + image: { raw: "image" }, + twitter: { raw: "twitter" }, + }) + .signAsync(baltathar) + ); + }); + + it({ + id: "T01", + title: "should retrieve registrars", + test: async function () { + const registrars = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "registrars", + args: [], + })) as any; + + expect(registrars.length).to.equal(1); + expect(registrars[0].isValid).to.be.true; + expect(registrars[0].index).to.equal(0); + expect(registrars[0].account).to.equal(alith.address); + expect(registrars[0].fee).to.equal(100n); + expect(registrars[0].fields.display).to.be.true; + expect(registrars[0].fields.web).to.be.true; + expect(registrars[0].fields.legal).to.be.true; + expect(registrars[0].fields.riot).to.be.false; + expect(registrars[0].fields.email).to.be.false; + expect(registrars[0].fields.pgpFingerprint).to.be.false; + expect(registrars[0].fields.image).to.be.false; + expect(registrars[0].fields.twitter).to.be.false; + }, + }); + + it({ + id: "T02", + title: "should retrieve identity", + test: async function () { + const identity = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "identity", + args: [baltathar.address], + })) as any; + + expect(identity.isValid).to.be.true; + expect(identity.judgements).to.be.empty; + expect(identity.deposit).to.equal(1034200000000000000n); + expect(identity.info.additional.length).to.equal(1); + expect(identity.info.additional[0].key.hasData).to.be.true; + expect(identity.info.additional[0].key.value).to.equal(toHex("discord")); + expect(identity.info.additional[0].value.hasData).to.be.true; + expect(identity.info.additional[0].value.value).to.equal(toHex("my-discord")); + expect(identity.info.display.hasData).to.be.true; + expect(identity.info.display.value).to.equal(toHex("display")); + expect(identity.info.legal.hasData).to.be.true; + expect(identity.info.legal.value).to.equal(toHex("legal")); + expect(identity.info.web.hasData).to.be.true; + expect(identity.info.web.value).to.equal(toHex("web")); + expect(identity.info.riot.hasData).to.be.true; + expect(identity.info.riot.value).to.equal(toHex("riot")); + expect(identity.info.email.hasData).to.be.true; + expect(identity.info.email.value).to.equal(toHex("email")); + expect(identity.info.hasPgpFingerprint).to.be.true; + expect(identity.info.pgpFingerprint).to.equal( + toHex(Uint8Array.from(new Array(20).fill(1))) + ); + expect(identity.info.image.hasData).to.be.true; + expect(identity.info.image.value).to.equal(toHex("image")); + expect(identity.info.twitter.hasData).to.be.true; + expect(identity.info.twitter.value).to.equal(toHex("twitter")); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity10.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity10.ts new file mode 100644 index 00000000..d58b871b --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity10.ts @@ -0,0 +1,61 @@ +/** + * Identity precompile tests - set account id + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { alith, charleth } from "@moonwall/util"; +import { expectEVMResult, PRECOMPILE_IDENTITY_ADDRESS } from "../../../../helpers"; + +describeSuite({ + id: "D030310", + title: "Precompiles - Identity precompile - set account id", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.identity.addRegistrar(alith.address)) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "setAccountId", + rawTxOnly: true, + args: [0, charleth.address], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + }); + + it({ + id: "T01", + title: "should retrieve the registrar", + test: async function () { + const registrars = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "registrars", + })) as any; + + expect(registrars.length).to.equal(1); + expect(registrars[0].isValid).to.be.true; + expect(registrars[0].index).to.equal(0); + expect(registrars[0].account).to.equal(charleth.address); + expect(registrars[0].fee).to.equal(0n); + expect(registrars[0].fields.display).to.be.false; + expect(registrars[0].fields.web).to.be.false; + expect(registrars[0].fields.legal).to.be.false; + expect(registrars[0].fields.riot).to.be.false; + expect(registrars[0].fields.email).to.be.false; + expect(registrars[0].fields.pgpFingerprint).to.be.false; + expect(registrars[0].fields.image).to.be.false; + expect(registrars[0].fields.twitter).to.be.false; + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity11.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity11.ts new file mode 100644 index 00000000..d8d2d726 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity11.ts @@ -0,0 +1,95 @@ +/** + * Identity precompile tests - add sub + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { BALTATHAR_PRIVATE_KEY, baltathar, charleth } from "@moonwall/util"; +import { decodeEventLog, toHex } from "viem"; +import { + PRECOMPILE_IDENTITY_ADDRESS, + expectEVMResult, + expectSubstrateEvent, +} from "../../../../helpers"; + +describeSuite({ + id: "D030311", + title: "Precompiles - Identity precompile - add sub", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.identity.setIdentity({ + display: { raw: "display" }, + }) + .signAsync(baltathar) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "addSub", + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + args: [ + charleth.address, + { + hasData: true, + value: toHex("test"), + }, + ], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: fetchCompiledContract("Identity").abi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + + expect(evmLog.eventName).to.equal("SubIdentityAdded"); + expect(evmLog.args.sub).to.equal(charleth.address); + expect(evmLog.args.main).to.equal(baltathar.address); + }); + + it({ + id: "T01", + title: "should retrieve subs", + test: async function () { + const subs = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "subsOf", + args: [baltathar.address], + })) as any; + + expect(subs.deposit).to.equal(1005300000000000000n); + expect(subs.accounts).to.have.length(1); + expect(subs.accounts[0]).to.be.equal(charleth.address); + }, + }); + + it({ + id: "T02", + title: "should retrieve super", + test: async function () { + const superOf = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "superOf", + args: [charleth.address], + })) as any; + + expect(superOf.isValid).to.be.true; + expect(superOf.account).to.be.equal(baltathar.address); + expect(superOf.data.hasData).to.be.true; + expect(superOf.data.value).to.be.equal(toHex("test")); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity12.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity12.ts new file mode 100644 index 00000000..f656e399 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity12.ts @@ -0,0 +1,87 @@ +/** + * Identity precompile tests - rename sub + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { BALTATHAR_PRIVATE_KEY, baltathar, charleth } from "@moonwall/util"; +import { toHex } from "viem"; +import { PRECOMPILE_IDENTITY_ADDRESS, expectEVMResult } from "../../../../helpers"; + +describeSuite({ + id: "D030312", + title: "Precompiles - Identity precompile - rename sub", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.identity.setIdentity({ + display: { raw: "display" }, + }) + .signAsync(baltathar) + ); + await context.createBlock( + context + .polkadotJs() + .tx.identity.addSub(charleth.address, { Raw: "test" }) + .signAsync(baltathar) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "renameSub", + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + args: [ + charleth.address, + { + hasData: true, + value: toHex("foobar"), + }, + ], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + }); + + it({ + id: "T01", + title: "should retrieve subs", + test: async function () { + const subs = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "subsOf", + args: [baltathar.address], + })) as any; + + expect(subs.deposit).to.equal(1005300000000000000n); + expect(subs.accounts).to.have.length(1); + expect(subs.accounts[0]).to.be.equal(charleth.address); + }, + }); + + it({ + id: "T02", + title: "should retrieve super", + test: async function () { + const superOf = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "superOf", + args: [charleth.address], + })) as any; + + expect(superOf.isValid).to.be.true; + expect(superOf.account).to.be.equal(baltathar.address); + expect(superOf.data.hasData).to.be.true; + expect(superOf.data.value).to.be.equal(toHex("foobar")); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity13.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity13.ts new file mode 100644 index 00000000..bf140c82 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity13.ts @@ -0,0 +1,76 @@ +/** + * Identity precompile tests - remove sub + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { BALTATHAR_PRIVATE_KEY, baltathar, charleth } from "@moonwall/util"; +import { decodeEventLog } from "viem"; +import { + PRECOMPILE_IDENTITY_ADDRESS, + expectEVMResult, + expectSubstrateEvent, +} from "../../../../helpers"; + +describeSuite({ + id: "D030313", + title: "Precompiles - Identity precompile - remove sub", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.identity.setIdentity({ + display: { raw: "display" }, + }) + .signAsync(baltathar) + ); + await context.createBlock( + context + .polkadotJs() + .tx.identity.addSub(charleth.address, { Raw: "test" }) + .signAsync(baltathar) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "removeSub", + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + args: [charleth.address], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: fetchCompiledContract("Identity").abi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + + expect(evmLog.eventName).to.equal("SubIdentityRemoved"); + expect(evmLog.args.sub).to.equal(charleth.address); + expect(evmLog.args.main).to.equal(baltathar.address); + }); + + it({ + id: "T01", + title: "should have no subs", + test: async function () { + const subs = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "subsOf", + args: [baltathar.address], + })) as any; + + expect(subs.deposit).to.be.equal(0n); + expect(subs.accounts).to.be.empty; + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity14.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity14.ts new file mode 100644 index 00000000..d2569605 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity14.ts @@ -0,0 +1,73 @@ +/** + * Identity precompile tests - quit sub + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { CHARLETH_PRIVATE_KEY, baltathar, charleth } from "@moonwall/util"; +import { decodeEventLog } from "viem"; +import { + PRECOMPILE_IDENTITY_ADDRESS, + expectEVMResult, + expectSubstrateEvent, +} from "../../../../helpers"; + +describeSuite({ + id: "D030314", + title: "Precompiles - Identity precompile - quit sub", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.identity.setIdentity({ + display: { raw: "display" }, + }) + .signAsync(baltathar) + ); + await context.createBlock( + context + .polkadotJs() + .tx.identity.addSub(charleth.address, { Raw: "test" }) + .signAsync(baltathar) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "quitSub", + privateKey: CHARLETH_PRIVATE_KEY, + rawTxOnly: true, + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: fetchCompiledContract("Identity").abi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + + expect(evmLog.eventName).to.equal("SubIdentityRevoked"); + expect(evmLog.args.sub).to.equal(charleth.address); + }); + + it({ + id: "T01", + title: "should have no super", + test: async function () { + const superOf = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "superOf", + args: [charleth.address], + })) as any; + + expect(superOf.isValid).to.be.false; + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity2.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity2.ts new file mode 100644 index 00000000..97c2a795 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity2.ts @@ -0,0 +1,102 @@ +/** + * Identity precompile tests - set identity + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { BALTATHAR_PRIVATE_KEY, baltathar } from "@moonwall/util"; +import { decodeEventLog, toHex } from "viem"; +import { + PRECOMPILE_IDENTITY_ADDRESS, + expectEVMResult, + expectSubstrateEvent, +} from "../../../../helpers"; + +describeSuite({ + id: "D030302", + title: "Precompiles - Identity precompile - set identity", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "setIdentity", + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + args: [ + { + additional: [ + { + key: { hasData: true, value: toHex("discord") }, + value: { hasData: true, value: toHex("my-discord") }, + }, + ], + display: { hasData: true, value: toHex("display") }, + legal: { hasData: true, value: toHex("legal") }, + web: { hasData: true, value: toHex("web") }, + riot: { hasData: true, value: toHex("riot") }, + email: { hasData: true, value: toHex("email") }, + hasPgpFingerprint: true, + pgpFingerprint: toHex(Uint8Array.from(new Array(20).fill(1))), + image: { hasData: true, value: toHex("image") }, + twitter: { hasData: true, value: toHex("twitter") }, + }, + ], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: fetchCompiledContract("Identity").abi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + + expect(evmLog.eventName).to.equal("IdentitySet"); + expect(evmLog.args.who).to.equal(baltathar.address); + }); + + it({ + id: "T01", + title: "should retrieve newly set identity", + test: async function () { + const identity = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "identity", + args: [baltathar.address], + })) as any; + + expect(identity.isValid).to.be.true; + expect(identity.judgements).to.be.empty; + expect(identity.deposit).to.equal(1034200000000000000n); + expect(identity.info.additional.length).to.equal(1); + expect(identity.info.additional[0].key.hasData).to.be.true; + expect(identity.info.additional[0].key.value).to.equal(toHex("discord")); + expect(identity.info.additional[0].value.hasData).to.be.true; + expect(identity.info.additional[0].value.value).to.equal(toHex("my-discord")); + expect(identity.info.display.hasData).to.be.true; + expect(identity.info.display.value).to.equal(toHex("display")); + expect(identity.info.legal.hasData).to.be.true; + expect(identity.info.legal.value).to.equal(toHex("legal")); + expect(identity.info.web.hasData).to.be.true; + expect(identity.info.web.value).to.equal(toHex("web")); + expect(identity.info.riot.hasData).to.be.true; + expect(identity.info.riot.value).to.equal(toHex("riot")); + expect(identity.info.email.hasData).to.be.true; + expect(identity.info.email.value).to.equal(toHex("email")); + expect(identity.info.hasPgpFingerprint).to.be.true; + expect(identity.info.pgpFingerprint).to.equal( + toHex(Uint8Array.from(new Array(20).fill(1))) + ); + expect(identity.info.image.hasData).to.be.true; + expect(identity.info.image.value).to.equal(toHex("image")); + expect(identity.info.twitter.hasData).to.be.true; + expect(identity.info.twitter.value).to.equal(toHex("twitter")); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity3.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity3.ts new file mode 100644 index 00000000..7702797f --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity3.ts @@ -0,0 +1,67 @@ +/** + * Identity precompile tests - clear identity + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { BALTATHAR_PRIVATE_KEY, baltathar } from "@moonwall/util"; +import { decodeEventLog } from "viem"; +import { + PRECOMPILE_IDENTITY_ADDRESS, + expectEVMResult, + expectSubstrateEvent, +} from "../../../../helpers"; + +describeSuite({ + id: "D030303", + title: "Precompiles - Identity precompile - clear identity", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock([ + context + .polkadotJs() + .tx.identity.setIdentity({ + display: { raw: "display" }, + }) + .signAsync(baltathar), + ]); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "clearIdentity", + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: fetchCompiledContract("Identity").abi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + + expect(evmLog.eventName).to.equal("IdentityCleared"); + expect(evmLog.args.who).to.equal(baltathar.address); + }); + + it({ + id: "T01", + title: "should have no identity", + test: async function () { + const identity = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "identity", + args: [baltathar.address], + })) as any; + + expect(identity.isValid).to.be.false; + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity4.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity4.ts new file mode 100644 index 00000000..16f5db4a --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity4.ts @@ -0,0 +1,82 @@ +/** + * Identity precompile tests - request judgement + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { BALTATHAR_PRIVATE_KEY, alith, baltathar } from "@moonwall/util"; +import { decodeEventLog, toHex } from "viem"; +import { + PRECOMPILE_IDENTITY_ADDRESS, + expectEVMResult, + expectSubstrateEvent, +} from "../../../../helpers"; + +describeSuite({ + id: "D030304", + title: "Precompiles - Identity precompile - request judgement", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.identity.addRegistrar(alith.address)) + ); + await context.createBlock([ + context.polkadotJs().tx.identity.setFee(0, 100n), + context + .polkadotJs() + .tx.identity.setIdentity({ + display: { raw: "display" }, + }) + .signAsync(baltathar), + ]); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "requestJudgement", + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + args: [0, 100], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: fetchCompiledContract("Identity").abi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + + expect(evmLog.eventName).to.equal("JudgementRequested"); + expect(evmLog.args.who).to.equal(baltathar.address); + expect(evmLog.args.registrarIndex).to.equal(0); + }); + + it({ + id: "T01", + title: "should retrieve requested judgement as part of identity", + test: async function () { + const identity = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "identity", + args: [baltathar.address], + })) as any; + + expect(identity.isValid).to.be.true; + expect(identity.judgements).to.have.length(1); + expect(identity.judgements[0].registrarIndex).to.equal(0); + expect(identity.judgements[0].judgement.isFeePaid).to.be.true; + expect(identity.judgements[0].judgement.feePaidDeposit).to.equal(100n); + expect(identity.deposit).to.equal(1027400000000000000n); + expect(identity.info.display.hasData).to.be.true; + expect(identity.info.display.value).to.equal(toHex("display")); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity5.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity5.ts new file mode 100644 index 00000000..5c10543b --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity5.ts @@ -0,0 +1,82 @@ +/** + * Identity precompile tests - cancel requested judgement + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { BALTATHAR_PRIVATE_KEY, alith, baltathar } from "@moonwall/util"; +import { decodeEventLog, toHex } from "viem"; +import { + PRECOMPILE_IDENTITY_ADDRESS, + expectEVMResult, + expectSubstrateEvent, +} from "../../../../helpers"; + +describeSuite({ + id: "D030305", + title: "Precompiles - Identity precompile - cancel requested judgement", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.identity.addRegistrar(alith.address)) + ); + await context.createBlock([ + context.polkadotJs().tx.identity.setFee(0, 100n), + context + .polkadotJs() + .tx.identity.setIdentity({ + display: { raw: "display" }, + }) + .signAsync(baltathar), + ]); + await context.createBlock( + context.polkadotJs().tx.identity.requestJudgement(0, 1000n).signAsync(baltathar) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "cancelRequest", + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + args: [0], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: fetchCompiledContract("Identity").abi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + + expect(evmLog.eventName).to.equal("JudgementUnrequested"); + expect(evmLog.args.who).to.equal(baltathar.address); + expect(evmLog.args.registrarIndex).to.equal(0); + }); + + it({ + id: "T01", + title: "should have no requested judgement as part of identity", + test: async function () { + const identity = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "identity", + args: [baltathar.address], + })) as any; + + expect(identity.isValid).to.be.true; + expect(identity.judgements).to.be.empty; + expect(identity.deposit).to.equal(1027400000000000000n); + expect(identity.info.display.hasData).to.be.true; + expect(identity.info.display.value).to.equal(toHex("display")); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity6.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity6.ts new file mode 100644 index 00000000..8591a33f --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity6.ts @@ -0,0 +1,98 @@ +/** + * Identity precompile tests - provide judgement + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { alith, baltathar } from "@moonwall/util"; +import { decodeEventLog, toHex } from "viem"; +import { + PRECOMPILE_IDENTITY_ADDRESS, + expectEVMResult, + expectSubstrateEvent, +} from "../../../../helpers"; + +describeSuite({ + id: "D030306", + title: "Precompiles - Identity precompile - provide judgement", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.identity.addRegistrar(alith.address)) + ); + const identityData = { + display: { raw: "display" }, + }; + await context.createBlock([ + context.polkadotJs().tx.identity.setFee(0, 100n), + context.polkadotJs().tx.identity.setIdentity(identityData).signAsync(baltathar), + ]); + await context.createBlock( + context.polkadotJs().tx.identity.requestJudgement(0, 1000n).signAsync(baltathar) + ); + + const identityHash = context + .polkadotJs() + .registry.createType("PalletIdentityLegacyIdentityInfo", identityData) + .hash.toHex(); + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "provideJudgement", + rawTxOnly: true, + args: [ + 0, + baltathar.address, + { + isUnknown: false, + isFeePaid: false, + feePaidDeposit: 0, + isReasonable: false, + isKnownGood: true, + isOutOfDate: false, + isLowQuality: false, + isErroneous: false, + }, + identityHash, + ], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: fetchCompiledContract("Identity").abi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + + expect(evmLog.eventName).to.equal("JudgementGiven"); + expect(evmLog.args.target).to.equal(baltathar.address); + expect(evmLog.args.registrarIndex).to.equal(0); + }); + + it({ + id: "T01", + title: "should have provided judgement as part of identity", + test: async function () { + const identity = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "identity", + args: [baltathar.address], + })) as any; + + expect(identity.isValid).to.be.true; + expect(identity.judgements).to.have.length(1); + expect(identity.judgements[0].judgement.isKnownGood).to.be.true; + expect(identity.deposit).to.equal(1027400000000000000n); + expect(identity.info.display.hasData).to.be.true; + expect(identity.info.display.value).to.equal(toHex("display")); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity7.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity7.ts new file mode 100644 index 00000000..0675dbc1 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity7.ts @@ -0,0 +1,82 @@ +/** + * Identity precompile tests - set subs + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { BALTATHAR_PRIVATE_KEY, baltathar, charleth } from "@moonwall/util"; +import { toHex } from "viem"; +import { PRECOMPILE_IDENTITY_ADDRESS, expectEVMResult } from "../../../../helpers"; + +describeSuite({ + id: "D030307", + title: "Precompiles - Identity precompile - set subs", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.identity.setIdentity({ + display: { raw: "display" }, + }) + .signAsync(baltathar) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "setSubs", + privateKey: BALTATHAR_PRIVATE_KEY, + rawTxOnly: true, + args: [ + [ + { + account: charleth.address, + data: { hasData: true, value: toHex("test") }, + }, + ], + ], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + }); + + it({ + id: "T01", + title: "should retrieve subs", + test: async function () { + const subs = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "subsOf", + args: [baltathar.address], + })) as any; + + expect(subs.deposit).to.equal(1005300000000000000n); + expect(subs.accounts).to.have.length(1); + expect(subs.accounts[0]).to.be.equal(charleth.address); + }, + }); + + it({ + id: "T02", + title: "should retrieve super", + test: async function () { + const superOf = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "superOf", + args: [charleth.address], + })) as any; + + expect(superOf.isValid).to.be.true; + expect(superOf.account).to.be.equal(baltathar.address); + expect(superOf.data.hasData).to.be.true; + expect(superOf.data.value).to.be.equal(toHex("test")); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity8.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity8.ts new file mode 100644 index 00000000..549001a5 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity8.ts @@ -0,0 +1,61 @@ +/** + * Identity precompile tests - set fee + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { alith } from "@moonwall/util"; +import { expectEVMResult, PRECOMPILE_IDENTITY_ADDRESS } from "../../../../helpers"; + +describeSuite({ + id: "D030308", + title: "Precompiles - Identity precompile - set fee", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.identity.addRegistrar(alith.address)) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "setFee", + rawTxOnly: true, + args: [0, 100], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + }); + + it({ + id: "T01", + title: "should retrieve the registrar", + test: async function () { + const registrars = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "registrars", + })) as any; + + expect(registrars.length).to.equal(1); + expect(registrars[0].isValid).to.be.true; + expect(registrars[0].index).to.equal(0); + expect(registrars[0].account).to.equal(alith.address); + expect(registrars[0].fee).to.equal(100n); + expect(registrars[0].fields.display).to.be.false; + expect(registrars[0].fields.web).to.be.false; + expect(registrars[0].fields.legal).to.be.false; + expect(registrars[0].fields.riot).to.be.false; + expect(registrars[0].fields.email).to.be.false; + expect(registrars[0].fields.pgpFingerprint).to.be.false; + expect(registrars[0].fields.image).to.be.false; + expect(registrars[0].fields.twitter).to.be.false; + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity9.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity9.ts new file mode 100644 index 00000000..fbfb4ce1 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-identity9.ts @@ -0,0 +1,73 @@ +/** + * Identity precompile tests - set fields + * Adapted from Moonbeam test suite + */ + +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { alith } from "@moonwall/util"; +import { expectEVMResult, PRECOMPILE_IDENTITY_ADDRESS } from "../../../../helpers"; + +describeSuite({ + id: "D030309", + title: "Precompiles - Identity precompile - set fields", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + beforeAll(async function () { + await context.createBlock( + context + .polkadotJs() + .tx.sudo.sudo(context.polkadotJs().tx.identity.addRegistrar(alith.address)) + ); + + const block = await context.createBlock( + await context.writeContract!({ + contractName: "Identity", + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + functionName: "setFields", + rawTxOnly: true, + args: [ + 0, + { + display: true, + web: true, + legal: true, + riot: true, + email: true, + pgpFingerprint: true, + image: true, + twitter: true, + }, + ], + }) + ); + + expectEVMResult(block.result!.events, "Succeed"); + }); + + it({ + id: "T01", + title: "should retrieve the registrar", + test: async function () { + const registrars = (await context.readContract!({ + contractAddress: PRECOMPILE_IDENTITY_ADDRESS, + contractName: "Identity", + functionName: "registrars", + })) as any; + + expect(registrars.length).to.equal(1); + expect(registrars[0].isValid).to.be.true; + expect(registrars[0].index).to.equal(0); + expect(registrars[0].account).to.equal(alith.address); + expect(registrars[0].fee).to.equal(0n); + expect(registrars[0].fields.display).to.be.true; + expect(registrars[0].fields.web).to.be.true; + expect(registrars[0].fields.legal).to.be.true; + expect(registrars[0].fields.riot).to.be.true; + expect(registrars[0].fields.email).to.be.true; + expect(registrars[0].fields.pgpFingerprint).to.be.true; + expect(registrars[0].fields.image).to.be.true; + expect(registrars[0].fields.twitter).to.be.true; + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-modexp.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-modexp.ts new file mode 100644 index 00000000..fe4e6d07 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-modexp.ts @@ -0,0 +1,824 @@ +import "@moonbeam-network/api-augment"; +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { EXTRINSIC_GAS_LIMIT, createViemTransaction } from "@moonwall/util"; +import { hexToU8a, u8aToHex } from "@polkadot/util"; +import { expectEVMResult, testVectors } from "../../../../helpers"; +import { calculateEIP7623Gas } from "../../../../helpers/fees"; + +const MODEXP_PRECOMPILE_ADDRESS = "0x0000000000000000000000000000000000000005"; + +// TODO: Gas tests (T03, T05-T21) are skipped because DataHaven uses Frontier stable2412 +// which doesn't include EIP-7623 gas calculation. Re-enable these tests after upgrading +// to Polkadot stable2506 (which includes the newer Frontier with EIP-7623 support). + +describeSuite({ + id: "D030108", + title: "Precompiles - modexp", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + let hasherAddress: `0x${string}`; + + beforeAll(async function () { + const { contractAddress } = await context.deployContract!("HasherChecker"); + hasherAddress = contractAddress; + }); + + it({ + id: "T01", + title: "should be accessible from a smart contract", + test: async function () { + const rawTx = await context.writeContract!({ + contractName: "HasherChecker", + contractAddress: hasherAddress, + functionName: "modExpChecker", + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + expectEVMResult(result!.events, "Succeed"); + }, + }); + + it({ + id: "T02", + title: "EIP example 1 - calculation", + test: async function () { + const rawTx = await context.writeContract!({ + contractName: "HasherChecker", + contractAddress: hasherAddress, + functionName: "modExpVerify", + args: [ + "3", + "115792089237316195423570985008687907853269984665640564039457584007908834671662", + "115792089237316195423570985008687907853269984665640564039457584007908834671663", + ], + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + expectEVMResult(result!.events, "Succeed"); + + expect( + await context.readContract!({ + contractAddress: hasherAddress, + contractName: "HasherChecker", + functionName: "getResult", + }) + ).to.be.equals(1n); + }, + }); + + it({ + id: "T03", + title: "EIP example 1 - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 3728n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000001" + // base length + "0000000000000000000000000000000000000000000000000000000000000020" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000020" + // modulus length + "03" + // base + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + // exponent + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"; // modulus + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T04", + title: "EIP example 2", + test: async function () { + await context.writeContract!({ + contractName: "HasherChecker", + contractAddress: hasherAddress, + functionName: "modExpVerify", + args: [ + "0", + "115792089237316195423570985008687907853269984665640564039457584007908834671662", + "115792089237316195423570985008687907853269984665640564039457584007908834671663", + ], + }); + + await context.createBlock(); + expect( + await context.readContract!({ + contractAddress: hasherAddress, + contractName: "HasherChecker", + functionName: "getResult", + }) + ).toBe(0n); + }, + }); + + it({ + id: "T05", + title: "EIP example 2 - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 3728n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000000" + // base length + "0000000000000000000000000000000000000000000000000000000000000020" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000020" + // modulus length + // base length is zero so value is inferred zero + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + // exponent + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"; // modulus + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T06", + title: "nagydani-1-square - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 1869n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000040" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000040" + // modulus length + testVectors["nagydani-1-square"].base + + testVectors["nagydani-1-square"].exponent + + testVectors["nagydani-1-square"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T07", + title: "nagydani-1-qube - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 1869n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000040" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000040" + // modulus length + testVectors["nagydani-1-square"].base + + testVectors["nagydani-1-qube"].exponent + + testVectors["nagydani-1-qube"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T08", + title: "nagydani-1-pow0x10001 - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 2010n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000040" + // base length + "0000000000000000000000000000000000000000000000000000000000000003" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000040" + // modulus length + testVectors["nagydani-1-pow0x10001"].base + + testVectors["nagydani-1-pow0x10001"].exponent + + testVectors["nagydani-1-pow0x10001"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T09", + title: "nagydani-2-square - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 1869n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000080" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000080" + // modulus length + testVectors["nagydani-2-square"].base + + testVectors["nagydani-2-square"].exponent + + testVectors["nagydani-2-square"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T10", + title: "nagydani-2-qube - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 1869n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000080" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000080" + // modulus length + testVectors["nagydani-2-qube"].base + + testVectors["nagydani-2-qube"].exponent + + testVectors["nagydani-2-qube"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T11", + title: "nagydani-2-pow0x10001 - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 3034n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000080" + // base length + "0000000000000000000000000000000000000000000000000000000000000003" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000080" + // modulus length + testVectors["nagydani-2-pow0x10001"].base + + testVectors["nagydani-2-pow0x10001"].exponent + + testVectors["nagydani-2-pow0x10001"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T12", + title: "nagydani-3-square - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 2010n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000100" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000100" + // modulus length + testVectors["nagydani-3-square"].base + + testVectors["nagydani-3-square"].exponent + + testVectors["nagydani-3-square"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T13", + title: "nagydani-3-qube - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 2010n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000100" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000100" + // modulus length + testVectors["nagydani-3-qube"].base + + testVectors["nagydani-3-qube"].exponent + + testVectors["nagydani-3-qube"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T14", + title: "nagydani-3-pow0x10001 - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 7130n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000100" + // base length + "0000000000000000000000000000000000000000000000000000000000000003" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000100" + // modulus length + testVectors["nagydani-3-pow0x10001"].base + + testVectors["nagydani-3-pow0x10001"].exponent + + testVectors["nagydani-3-pow0x10001"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T15", + title: "nagydani-4-square - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 3034n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000200" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000200" + // modulus length + testVectors["nagydani-4-square"].base + + testVectors["nagydani-4-square"].exponent + + testVectors["nagydani-4-square"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T16", + title: "nagydani-4-qube - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 3034n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000200" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000200" + // modulus length + testVectors["nagydani-4-qube"].base + + testVectors["nagydani-4-qube"].exponent + + testVectors["nagydani-4-qube"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T17", + title: "nagydani-4-pow0x10001 - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 23514n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000200" + // base length + "0000000000000000000000000000000000000000000000000000000000000003" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000200" + // modulus length + testVectors["nagydani-4-pow0x10001"].base + + testVectors["nagydani-4-pow0x10001"].exponent + + testVectors["nagydani-4-pow0x10001"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T18", + title: "nagydani-5-square - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 7130n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000400" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000400" + // modulus length + testVectors["nagydani-5-square"].base + + testVectors["nagydani-5-square"].exponent + + testVectors["nagydani-5-square"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T19", + title: "nagydani-5-qube - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 7130n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000400" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000400" + // modulus length + testVectors["nagydani-5-qube"].base + + testVectors["nagydani-5-qube"].exponent + + testVectors["nagydani-5-qube"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T20", + title: "nagydani-5-pow0x10001 - gas", + modifier: "skip", + test: async function () { + const expectedModExpGasCost = 89749n; + const inputData = + "0000000000000000000000000000000000000000000000000000000000000400" + // base length + "0000000000000000000000000000000000000000000000000000000000000003" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000400" + // modulus length + testVectors["nagydani-5-pow0x10001"].base + + testVectors["nagydani-5-pow0x10001"].exponent + + testVectors["nagydani-5-pow0x10001"].modulus; + const byteArray = hexToU8a(inputData); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T21", + title: "Exponent > 32", + modifier: "skip", + test: async function () { + // We multiply by a factor of 20 for an even mod. + // See https://github.com/paritytech/frontier/pull/1017 + const expectedModExpGasCost = 7104n * 20n; + const byteArray = new Uint8Array([ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0x02, 0x0, 0x0, + 0xb3, 0x0, 0x0, 0x02, 0x0, 0x0, 0x7a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, + 0xfb, 0x0, 0x0, 0x0, 0x0, 0x04, 0x26, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 96, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0x02, 0x0, 0x0, 0xb3, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xf9, + ]); + const inputData = u8aToHex(byteArray); + const inputLength = byteArray.length; + const numZeroBytes = byteArray.filter((a) => a === 0).length; + const numNonZeroBytes = inputLength - numZeroBytes; + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: inputData, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + const isPrecompileCheckGas = 2368n; + const expectedGasUsed = calculateEIP7623Gas( + numZeroBytes, + numNonZeroBytes, + expectedModExpGasCost + isPrecompileCheckGas + ); + expect(receipt.gasUsed, "ModExp gas pricing mismatch").to.equal(expectedGasUsed); + }, + }); + + it({ + id: "T22", + title: "should pad input when too short", + test: async function () { + const inputData = + "0000000000000000000000000000000000000000000000000000000000000001" + // base length + "0000000000000000000000000000000000000000000000000000000000000001" + // exponent length + "0000000000000000000000000000000000000000000000000000000000000002" + // modulus length + "05" + // base + "03" + // exponent + "01"; // modulus + + const rawTxn = await createViemTransaction(context, { + to: MODEXP_PRECOMPILE_ADDRESS, + data: ("0x" + inputData) as `0x${string}`, + gas: EXTRINSIC_GAS_LIMIT, + }); + const { result } = await context.createBlock(rawTxn); + + const receipt = await context + .viem() + .getTransactionReceipt({ hash: result!.hash as `0x${string}` }); + expect(receipt.status).toBe("success"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-preimage.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-preimage.ts new file mode 100644 index 00000000..7e6cb95d --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-preimage.ts @@ -0,0 +1,106 @@ +import "@moonbeam-network/api-augment"; +import { beforeAll, beforeEach, describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { type Abi, decodeEventLog } from "viem"; +import { Preimage, expectEVMResult, expectSubstrateEvent } from "../../../../helpers"; + +// Each test is instantiating a new proposal (Not ideal for isolation but easier to write) +// Be careful to not reach the maximum number of proposals. +describeSuite({ + id: "D030501", + title: "Precompiles - Preimage precompile", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + let PreimageAbi: Abi; + let preimage: Preimage; + + beforeAll(async function () { + const { abi } = fetchCompiledContract("Preimage"); + PreimageAbi = abi; + }); + + beforeEach(async function () { + preimage = new Preimage(context); + }); + + it({ + id: "T01", + title: "should allow to note Preimage", + test: async function () { + const call = context.polkadotJs().tx.identity.setIdentity({ display: { raw: "Me" } }); + const block = await preimage.notePreimage(call.toHex()).block(); + + // Verifies the EVM Side + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: PreimageAbi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + expect(evmLog.eventName, "Wrong event").to.equal("PreimageNoted"); + expect(evmLog.args.hash).to.equal(call.hash.toHex()); + + // Verifies the Substrate side + const preImage = await context + .polkadotJs() + .query.preimage.preimageFor([call.hash.toHex(), 15]); + expect(preImage.unwrap().toHex()).to.equal(call.toHex()); + }, + }); + + it({ + id: "T02", + title: "should allow to unnote a Preimage", + test: async function () { + const call = context.polkadotJs().tx.identity.setIdentity({ display: { raw: "You" } }); + await preimage.notePreimage(call.toHex()).block(); + const block = await preimage.unnotePreimage(call.hash.toHex()).block(); + + // Verifies the EVM Side + expectEVMResult(block.result!.events, "Succeed"); + const { data } = expectSubstrateEvent(block, "evm", "Log"); + const evmLog = decodeEventLog({ + abi: PreimageAbi, + topics: data[0].topics.map((t) => t.toHex()) as any, + data: data[0].data.toHex(), + }) as any; + // context.readPrecompile!({functionName}) + expect(evmLog.eventName, "Wrong event").to.equal("PreimageUnnoted"); + expect(evmLog.args.hash).to.equal(call.hash.toHex()); + + // Verifies the Substrate side + const preImage = await context + .polkadotJs() + .query.preimage.preimageFor([call.hash.toHex(), 1000]); + expect(preImage.isNone).to.equal(true); + }, + }); + + it({ + id: "T03", + title: "should fail to note the same Preimage twice", + test: async function () { + const call = context.polkadotJs().tx.identity.setIdentity({ display: { raw: "Repeated" } }); + await preimage.notePreimage(call.toHex()).block(); + await expect( + async () => await preimage.notePreimage(call.toHex()).block(), + "Transaction should be reverted but instead preimage noted" + ).rejects.toThrowError("AlreadyNoted"); + }, + }); + + it({ + id: "T04", + title: "should fail to unnote a missing Preimage", + test: async function () { + const call = context + .polkadotJs() + .tx.identity.setIdentity({ display: { raw: "Missing Preimage" } }); + await expect( + async () => await preimage.unnotePreimage(call.hash.toHex()).block(), + "Transaction should be reverted but instead preimage unnoted" + ).rejects.toThrowError("NotNoted"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-proxy.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-proxy.ts new file mode 100644 index 00000000..62c39bb0 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-proxy.ts @@ -0,0 +1,544 @@ +/** + * Proxy precompile tests + * Tests the Proxy precompile API for managing proxy accounts via EVM + */ + +import { describeSuite, expect, fetchCompiledContract } from "@moonwall/cli"; +import { + ALITH_ADDRESS, + BALTATHAR_ADDRESS, + BALTATHAR_PRIVATE_KEY, + CHARLETH_ADDRESS, + CHARLETH_PRIVATE_KEY, + createViemTransaction, + sendRawTransaction, +} from "@moonwall/util"; +import { encodeFunctionData } from "viem"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { expectEVMResult, PRECOMPILE_PROXY_ADDRESS } from "../../../../helpers"; + +// Proxy type constants (matching Substrate proxy types from Proxy.sol) +const CONTRACT_PROXY_TYPE_ANY = 0; +const CONTRACT_PROXY_TYPE_NON_TRANSFER = 1; +const CONTRACT_PROXY_TYPE_GOVERNANCE = 2; +const CONTRACT_PROXY_TYPE_STAKING = 3; +const CONTRACT_PROXY_TYPE_CANCEL_PROXY = 4; +const CONTRACT_PROXY_TYPE_BALANCES = 5; + +// Invalid proxy type for testing error handling +const CONTRACT_PROXY_TYPE_INVALID = 99; + +describeSuite({ + id: "D030601", + title: "Precompile - Proxy", + foundationMethods: "dev", + testCases: ({ it, log, context }) => { + it({ + id: "T01", + title: "should succeed adding a proxy", + test: async () => { + const randomAccount = privateKeyToAccount(generatePrivateKey()).address; + + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + expectEVMResult(result!.events, "Succeed"); + + // Verify proxy was added via substrate + const proxies = await context.polkadotJs().query.proxy.proxies(ALITH_ADDRESS); + const hasProxy = proxies[0].some( + (p: any) => + p.delegate.toString() === randomAccount && p.proxyType.toString() === "Staking" + ); + expect(hasProxy).to.be.true; + }, + }); + + it({ + id: "T02", + title: "should fail re-adding the same proxy", + test: async () => { + const randomAccount = privateKeyToAccount(generatePrivateKey()).address; + + // First add + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + rawTxOnly: true, + }); + await context.createBlock(rawTx); + + // Second add should fail with exact error message + await expect( + async () => + await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + }) + ).rejects.toThrowError("Cannot add more than one proxy"); + }, + }); + + it({ + id: "T03", + title: "should fail removing non-existent proxy", + test: async () => { + const randomAccount = privateKeyToAccount(generatePrivateKey()).address; + + await expect( + async () => + await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "removeProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + }) + ).rejects.toThrowError(/NotFound/i); + }, + }); + + it({ + id: "T04", + title: "should succeed removing an existing proxy", + test: async () => { + const randomAccount = privateKeyToAccount(generatePrivateKey()).address; + + // Add proxy + const addTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + rawTxOnly: true, + }); + await context.createBlock(addTx); + + // Remove proxy + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "removeProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + expectEVMResult(result!.events, "Succeed"); + + // Verify proxy was removed + const proxies = await context.polkadotJs().query.proxy.proxies(ALITH_ADDRESS); + const hasProxy = proxies[0].some((p: any) => p.delegate.toString() === randomAccount); + expect(hasProxy).to.be.false; + }, + }); + + it({ + id: "T05", + title: "should succeed removing all proxies", + test: async () => { + // First ensure no proxies exist + const proxiesInitial = await context.polkadotJs().query.proxy.proxies(ALITH_ADDRESS); + if (proxiesInitial[0].length > 0) { + const clearTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "removeProxies", + rawTxOnly: true, + }); + await context.createBlock(clearTx); + } + + // Add exactly two proxies + const account1 = privateKeyToAccount(generatePrivateKey()).address; + const account2 = privateKeyToAccount(generatePrivateKey()).address; + + const addTx1 = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [account1, CONTRACT_PROXY_TYPE_STAKING, 0], + rawTxOnly: true, + }); + await context.createBlock(addTx1); + + const addTx2 = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [account2, CONTRACT_PROXY_TYPE_GOVERNANCE, 0], + rawTxOnly: true, + }); + await context.createBlock(addTx2); + + const proxiesBefore = await context.polkadotJs().query.proxy.proxies(ALITH_ADDRESS); + expect(proxiesBefore[0].length).toBe(2); + + // Remove all proxies + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "removeProxies", + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + expectEVMResult(result!.events, "Succeed"); + + const proxiesAfter = await context.polkadotJs().query.proxy.proxies(ALITH_ADDRESS); + expect(proxiesAfter[0].length).toBe(0); + }, + }); + + it({ + id: "T06", + title: "should correctly report proxy status via isProxy", + test: async () => { + const randomAccount = privateKeyToAccount(generatePrivateKey()).address; + + // Check isProxy returns false before adding any proxy + const isProxyBefore = await context.readContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "isProxy", + args: [ALITH_ADDRESS, randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + }); + expect(isProxyBefore).to.be.false; + + // Add proxy + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + rawTxOnly: true, + }); + await context.createBlock(rawTx); + + // Check isProxy returns true for correct parameters + const isProxy = await context.readContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "isProxy", + args: [ALITH_ADDRESS, randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + }); + expect(isProxy).to.be.true; + + // Check isProxy returns false for wrong type + const isProxyWrongType = await context.readContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "isProxy", + args: [ALITH_ADDRESS, randomAccount, CONTRACT_PROXY_TYPE_ANY, 0], + }); + expect(isProxyWrongType).to.be.false; + + // Check isProxy returns false for wrong delay + const isProxyWrongDelay = await context.readContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "isProxy", + args: [ALITH_ADDRESS, randomAccount, CONTRACT_PROXY_TYPE_STAKING, 2], + }); + expect(isProxyWrongDelay).to.be.false; + }, + }); + + it({ + id: "T07", + title: "should reject proxy call from non-proxy account", + test: async () => { + // BALTATHAR tries to make a proxy call on behalf of ALITH without being a proxy + const { abi } = fetchCompiledContract("Proxy"); + await expect( + async () => + await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "proxy", + args: [ALITH_ADDRESS, CHARLETH_ADDRESS, "0x00"], + privateKey: BALTATHAR_PRIVATE_KEY, + }) + ).rejects.toThrowError("Not proxy"); + }, + }); + + it({ + id: "T08", + title: "should allow proxy call from valid proxy account", + test: async () => { + const privateKey = generatePrivateKey(); + const randomAccount = privateKeyToAccount(privateKey).address; + + // Add BALTATHAR as proxy for ALITH with Any type + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [BALTATHAR_ADDRESS, CONTRACT_PROXY_TYPE_ANY, 0], + rawTxOnly: true, + }); + await context.createBlock(rawTx); + + // Use BALTATHAR to transfer value on behalf of ALITH + const { abi } = fetchCompiledContract("Proxy"); + const proxyTx = await createViemTransaction(context, { + to: PRECOMPILE_PROXY_ADDRESS, + privateKey: BALTATHAR_PRIVATE_KEY, + value: 1000n, + data: encodeFunctionData({ + abi, + functionName: "proxy", + args: [ALITH_ADDRESS, randomAccount, "0x00"], + }), + }); + const txHash = (await sendRawTransaction(context, proxyTx)) as `0x${string}`; + + // Create two blocks to ensure the transaction is included + await context.createBlock(); + await context.createBlock(); + + const receipt = await context.viem().getTransactionReceipt({ hash: txHash }); + expect(receipt.status).toBe("success"); + + // Verify transfer happened + expect(await context.viem().getBalance({ address: randomAccount })).toBe(1000n); + }, + }); + + it({ + id: "T09", + title: "should succeed with proxyForceType for matching proxy type", + test: async () => { + const privateKey = generatePrivateKey(); + const randomAccount = privateKeyToAccount(privateKey).address; + + // Add CHARLETH as Balances proxy for ALITH + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [CHARLETH_ADDRESS, CONTRACT_PROXY_TYPE_BALANCES, 0], + rawTxOnly: true, + }); + await context.createBlock(rawTx); + + // Use CHARLETH to transfer value on behalf of ALITH using proxyForceType + const { abi } = fetchCompiledContract("Proxy"); + const proxyTx = await createViemTransaction(context, { + to: PRECOMPILE_PROXY_ADDRESS, + privateKey: CHARLETH_PRIVATE_KEY, + value: 500n, + data: encodeFunctionData({ + abi, + functionName: "proxyForceType", + args: [ALITH_ADDRESS, CONTRACT_PROXY_TYPE_BALANCES, randomAccount, "0x00"], + }), + }); + const { result } = await context.createBlock(proxyTx); + expectEVMResult(result!.events, "Succeed"); + + // Verify transfer happened + expect(await context.viem().getBalance({ address: randomAccount })).toBe(500n); + }, + }); + + it({ + id: "T10", + title: "should fail proxyForceType with mismatched proxy type", + test: async () => { + // CHARLETH is a Balances proxy for ALITH (from T09 or set up here) + const proxies = await context.polkadotJs().query.proxy.proxies(ALITH_ADDRESS); + const hasBalancesProxy = proxies[0].some( + (p: any) => + p.delegate.toString() === CHARLETH_ADDRESS && p.proxyType.toString() === "Balances" + ); + + if (!hasBalancesProxy) { + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [CHARLETH_ADDRESS, CONTRACT_PROXY_TYPE_BALANCES, 0], + rawTxOnly: true, + }); + await context.createBlock(rawTx); + } + + // Try to use proxyForceType with Governance type (CHARLETH only has Balances) + await expect( + async () => + await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "proxyForceType", + args: [ALITH_ADDRESS, CONTRACT_PROXY_TYPE_GOVERNANCE, BALTATHAR_ADDRESS, "0x00"], + privateKey: CHARLETH_PRIVATE_KEY, + }) + ).rejects.toThrowError(/Not proxy/i); + }, + }); + + it({ + id: "T11", + title: "should fail addProxy with invalid proxy type value", + test: async () => { + const randomAccount = privateKeyToAccount(generatePrivateKey()).address; + + await expect( + async () => + await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_INVALID, 0], + }) + ).rejects.toThrowError(/Failed decoding value to ProxyType/i); + }, + }); + + it({ + id: "T12", + title: "should succeed removeProxies when no proxies exist", + test: async () => { + // Use a fresh account that has no proxies + const privateKey = generatePrivateKey(); + const freshAccount = privateKeyToAccount(privateKey); + + // Fund the fresh account so it can make transactions + const fundTx = await createViemTransaction(context, { + to: freshAccount.address, + value: 10n * 10n ** 18n, // 10 tokens + }); + await context.createBlock(fundTx); + + // Verify no proxies exist for this account + const proxiesBefore = await context + .polkadotJs() + .query.proxy.proxies(freshAccount.address); + expect(proxiesBefore[0].length).toBe(0); + + // removeProxies should succeed even with no proxies + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "removeProxies", + privateKey: privateKey, + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + expectEVMResult(result!.events, "Succeed"); + }, + }); + + it({ + id: "T13", + title: "should correctly handle non-zero delay proxy", + test: async () => { + const randomAccount = privateKeyToAccount(generatePrivateKey()).address; + const delay = 5; + + // Add proxy with non-zero delay + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, delay], + rawTxOnly: true, + }); + const { result } = await context.createBlock(rawTx); + expectEVMResult(result!.events, "Succeed"); + + // Verify proxy was added with correct delay via substrate + const proxies = await context.polkadotJs().query.proxy.proxies(ALITH_ADDRESS); + const proxyEntry = proxies[0].find( + (p: any) => + p.delegate.toString() === randomAccount && p.proxyType.toString() === "Staking" + ); + expect(proxyEntry).toBeDefined(); + expect(proxyEntry.delay.toNumber()).toBe(delay); + + // isProxy should return true with correct delay + const isProxyCorrectDelay = await context.readContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "isProxy", + args: [ALITH_ADDRESS, randomAccount, CONTRACT_PROXY_TYPE_STAKING, delay], + }); + expect(isProxyCorrectDelay).to.be.true; + + // isProxy should return false with wrong delay (0) + const isProxyWrongDelay = await context.readContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "isProxy", + args: [ALITH_ADDRESS, randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + }); + expect(isProxyWrongDelay).to.be.false; + }, + }); + + it({ + id: "T14", + title: "should fail proxyForceType with invalid proxy type value", + test: async () => { + await expect( + async () => + await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "proxyForceType", + args: [ALITH_ADDRESS, CONTRACT_PROXY_TYPE_INVALID, CHARLETH_ADDRESS, "0x00"], + privateKey: BALTATHAR_PRIVATE_KEY, + }) + ).rejects.toThrowError(/Failed decoding value to ProxyType/i); + }, + }); + + it({ + id: "T15", + title: "should fail adding proxy with different type for same delegate", + test: async () => { + const randomAccount = privateKeyToAccount(generatePrivateKey()).address; + + // First add with Staking type + const rawTx = await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_STAKING, 0], + rawTxOnly: true, + }); + await context.createBlock(rawTx); + + // Try to add same delegate with different type (Any - more permissive) + await expect( + async () => + await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_ANY, 0], + }) + ).rejects.toThrowError("Cannot add more than one proxy"); + + // Try to add same delegate with different type (Governance - less permissive) + await expect( + async () => + await context.writeContract!({ + contractAddress: PRECOMPILE_PROXY_ADDRESS, + contractName: "Proxy", + functionName: "addProxy", + args: [randomAccount, CONTRACT_PROXY_TYPE_GOVERNANCE, 0], + }) + ).rejects.toThrowError("Cannot add more than one proxy"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-ripemd160.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-ripemd160.ts new file mode 100644 index 00000000..05167d35 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-ripemd160.ts @@ -0,0 +1,50 @@ +/** + * RIPEMD-160 precompile tests + * Adapted from Moonbeam test suite + */ + +import { describeSuite, expect } from "@moonwall/cli"; +import { toHex } from "viem"; +import { expectEVMResult } from "../../../../helpers"; + +describeSuite({ + id: "D030105", + title: "Precompiles - ripemd160", + foundationMethods: "dev", + testCases: ({ context, log, it }) => { + it({ + id: "T01", + title: "should be valid", + test: async function () { + expect( + ( + await context.viem().call({ + to: "0x0000000000000000000000000000000000000003", + data: toHex("Hello world!"), + }) + ).data + ).equals("0x0000000000000000000000007f772647d88750add82d8e1a7a3e5c0902a346a3"); + }, + }); + + it({ + id: "T02", + title: "should be accessible from a smart contract", + test: async function () { + const { contractAddress } = await context.deployContract!("HasherChecker"); + + // Execute the contract ripemd160 call + const rawTxn = await context.writeContract!({ + contractAddress, + contractName: "HasherChecker", + functionName: "ripemd160Check", + rawTxOnly: true, + }); + + const { result } = await context.createBlock(rawTxn); + + expectEVMResult(result!.events, "Succeed"); + }, + }); + }, +}); diff --git a/test/moonwall/suites/dev/stagenet/precompile/test-precompile-sha3fips.ts b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-sha3fips.ts new file mode 100644 index 00000000..c98efe96 --- /dev/null +++ b/test/moonwall/suites/dev/stagenet/precompile/test-precompile-sha3fips.ts @@ -0,0 +1,30 @@ +/** + * SHA3-FIPS precompile tests + * Adapted from Moonbeam test suite + */ + +import { describeSuite, expect } from "@moonwall/cli"; + +describeSuite({ + id: "D030106", + title: "Precompiles - sha3fips", + foundationMethods: "dev", + testCases: ({ context, it, log }) => { + // Test taken from https://github.com/binance-chain/bsc/pull/118 + it({ + id: "T01", + title: "sha3fips should be valid", + test: async function () { + expect( + ( + await context.viem().call({ + to: "0x0000000000000000000000000000000000000400", + data: ("0x0448250ebe88d77e0a12bcf530fe6a2cf1ac176945638d309b840d631940c93b78c2bd" + + "6d16f227a8877e3f1604cd75b9c5a8ab0cac95174a8a0a0f8ea9e4c10bca") as `0x${string}`, + }) + ).data + ).equals("0xc7647f7e251bf1bd70863c8693e93a4e77dd0c9a689073e987d51254317dc704"); + }, + }); + }, +});