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 <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
Co-authored-by: Ahmad Kaouk <ahmadkaouk.93@gmail.com>
This commit is contained in:
Steve Degosserie 2026-02-02 15:42:14 +01:00 committed by GitHub
parent ce24450b70
commit 506471db24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 4291 additions and 9 deletions

View file

@ -8,6 +8,7 @@
"**/*.yml",
"**/*.md",
"!node_modules/*",
"!**/moonwall/**/*",
"!target/*",
"!**/tmp/*",
"!**/*.spec.json",

View file

@ -258,7 +258,7 @@ pub fn dorothy() -> AccountId {
}
pub fn ethan() -> AccountId {
AccountId::from(hex!("Ff64d3F6efE2317EE2807d2235B1ac2AA69d9E87"))
AccountId::from(hex!("Ff64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB"))
}
pub fn frank() -> AccountId {

View file

@ -267,7 +267,7 @@ pub fn dorothy() -> AccountId {
}
pub fn ethan() -> AccountId {
AccountId::from(hex!("Ff64d3F6efE2317EE2807d2235B1ac2AA69d9E87"))
AccountId::from(hex!("Ff64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB"))
}
pub fn frank() -> AccountId {

View file

@ -265,7 +265,7 @@ pub fn dorothy() -> AccountId {
}
pub fn ethan() -> AccountId {
AccountId::from(hex!("Ff64d3F6efE2317EE2807d2235B1ac2AA69d9E87"))
AccountId::from(hex!("Ff64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB"))
}
pub fn frank() -> AccountId {

View file

@ -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;
}
}

View file

@ -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"
);
}
}

View file

@ -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<T> {
}
}
// 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;

View file

@ -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";

View file

@ -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;

View file

@ -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;

View file

@ -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<unknown> {
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<unknown> {
return await this.context.readPrecompile!(this.params);
}
}
class WritePrecompileCall extends PrecompileCall {
async tx(): Promise<unknown> {
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]);
}
}

View file

@ -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);
},
});
},
});

View file

@ -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);
},
});
}
},
});

View file

@ -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);
},
});
},
});

View file

@ -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);
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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"
// );
// });
// });

View file

@ -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"));
},
});
},
});

View file

@ -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;
},
});
},
});

View file

@ -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"));
},
});
},
});

View file

@ -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"));
},
});
},
});

View file

@ -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;
},
});
},
});

View file

@ -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;
},
});
},
});

View file

@ -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"));
},
});
},
});

View file

@ -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;
},
});
},
});

View file

@ -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"));
},
});
},
});

View file

@ -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"));
},
});
},
});

View file

@ -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"));
},
});
},
});

View file

@ -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"));
},
});
},
});

View file

@ -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;
},
});
},
});

View file

@ -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;
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});

View file

@ -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");
},
});
},
});