or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

accounts.mdcli.mdcontracts.mdconversion-testing.mdindex.mdnetwork.mdproject.md

conversion-testing.mddocs/

0

# Data Conversion & Testing

1

2

Type conversion utilities for seamless interaction between Python and Ethereum data types, plus integration with Hypothesis for property-based testing of smart contracts.

3

4

## Capabilities

5

6

### Data Type Classes

7

8

Specialized classes for handling Ethereum-specific data types with automatic conversion and validation.

9

10

```python { .api }

11

class Wei:

12

"""

13

Ethereum wei value with automatic unit conversion and arithmetic operations.

14

15

Supports all standard Ethereum units: wei, kwei, mwei, gwei, szabo, finney, ether.

16

"""

17

18

def __init__(self, value: Union[int, str, float]):

19

"""

20

Initialize Wei value.

21

22

Args:

23

value: Value in wei or string with units (e.g., "1 ether", "20 gwei")

24

"""

25

26

def __str__(self) -> str:

27

"""String representation in wei."""

28

29

def __int__(self) -> int:

30

"""Convert to integer wei value."""

31

32

def __float__(self) -> float:

33

"""Convert to float wei value."""

34

35

def __add__(self, other: Union['Wei', int, str]) -> 'Wei':

36

"""Add Wei values."""

37

38

def __sub__(self, other: Union['Wei', int, str]) -> 'Wei':

39

"""Subtract Wei values."""

40

41

def __mul__(self, other: Union[int, float]) -> 'Wei':

42

"""Multiply Wei value."""

43

44

def __truediv__(self, other: Union[int, float]) -> 'Wei':

45

"""Divide Wei value."""

46

47

def __eq__(self, other: Union['Wei', int, str]) -> bool:

48

"""Check equality with other Wei values."""

49

50

def __lt__(self, other: Union['Wei', int, str]) -> bool:

51

"""Compare Wei values."""

52

53

def to(self, unit: str) -> Union[int, float]:

54

"""

55

Convert to specific unit.

56

57

Args:

58

unit: Target unit (wei, kwei, mwei, gwei, szabo, finney, ether)

59

60

Returns:

61

Union[int, float]: Value in specified unit

62

"""

63

64

class Fixed:

65

"""

66

Fixed-point decimal number with configurable precision for financial calculations.

67

"""

68

69

def __init__(self, value: Union[int, str, float], digits: int = None):

70

"""

71

Initialize fixed-point number.

72

73

Args:

74

value: Numeric value

75

digits: Decimal places (auto-detect if None)

76

"""

77

78

def __str__(self) -> str:

79

"""String representation with full precision."""

80

81

def __int__(self) -> int:

82

"""Convert to integer (truncated)."""

83

84

def __float__(self) -> float:

85

"""Convert to float."""

86

87

def __add__(self, other: Union['Fixed', int, float]) -> 'Fixed':

88

"""Add Fixed values."""

89

90

def __sub__(self, other: Union['Fixed', int, float]) -> 'Fixed':

91

"""Subtract Fixed values."""

92

93

def __mul__(self, other: Union['Fixed', int, float]) -> 'Fixed':

94

"""Multiply Fixed values."""

95

96

def __truediv__(self, other: Union['Fixed', int, float]) -> 'Fixed':

97

"""Divide Fixed values."""

98

99

class EthAddress:

100

"""

101

Ethereum address with validation and checksum formatting.

102

"""

103

104

def __init__(self, address: str):

105

"""

106

Initialize Ethereum address.

107

108

Args:

109

address: Ethereum address string

110

111

Raises:

112

ValueError: If address format is invalid

113

"""

114

115

def __str__(self) -> str:

116

"""Checksum formatted address."""

117

118

def __eq__(self, other: Union['EthAddress', str]) -> bool:

119

"""Compare addresses (case-insensitive)."""

120

121

def is_checksum(self) -> bool:

122

"""Check if address uses valid checksum formatting."""

123

124

class HexString:

125

"""

126

Hexadecimal string with validation and conversion utilities.

127

"""

128

129

def __init__(self, data: Union[str, bytes, int]):

130

"""

131

Initialize hex string.

132

133

Args:

134

data: Data to convert to hexadecimal

135

"""

136

137

def __str__(self) -> str:

138

"""Hex string representation."""

139

140

def __bytes__(self) -> bytes:

141

"""Convert to bytes."""

142

143

def __int__(self) -> int:

144

"""Convert to integer."""

145

146

class ReturnValue:

147

"""

148

Smart contract return value wrapper with named field access.

149

"""

150

151

def __init__(self, values: tuple, abi: dict = None):

152

"""

153

Initialize return value wrapper.

154

155

Args:

156

values: Tuple of return values

157

abi: Function ABI for named access

158

"""

159

160

def __getitem__(self, index: Union[int, str]):

161

"""Access return value by index or name."""

162

163

def __iter__(self):

164

"""Iterate over return values."""

165

166

def __len__(self) -> int:

167

"""Number of return values."""

168

169

def dict(self) -> dict:

170

"""Convert to dictionary with named fields."""

171

```

172

173

### Conversion Functions

174

175

Utility functions for converting between Python and Ethereum data types with validation and error handling.

176

177

```python { .api }

178

def to_uint(value: Any, type_str: str = "uint256") -> int:

179

"""

180

Convert value to unsigned integer.

181

182

Args:

183

value: Value to convert

184

type_str: Solidity uint type (uint8, uint16, ..., uint256)

185

186

Returns:

187

int: Converted unsigned integer value

188

189

Raises:

190

ValueError: If value cannot be converted or is out of range

191

"""

192

193

def to_int(value: Any, type_str: str = "int256") -> int:

194

"""

195

Convert value to signed integer.

196

197

Args:

198

value: Value to convert

199

type_str: Solidity int type (int8, int16, ..., int256)

200

201

Returns:

202

int: Converted signed integer value

203

204

Raises:

205

ValueError: If value cannot be converted or is out of range

206

"""

207

208

def to_decimal(value: Any) -> Fixed:

209

"""

210

Convert value to fixed-point decimal.

211

212

Args:

213

value: Value to convert

214

215

Returns:

216

Fixed: Fixed-point decimal representation

217

"""

218

219

def to_address(value: Any) -> str:

220

"""

221

Convert value to Ethereum address.

222

223

Args:

224

value: Value to convert (string, bytes, int)

225

226

Returns:

227

str: Checksum formatted Ethereum address

228

229

Raises:

230

ValueError: If value cannot be converted to valid address

231

"""

232

233

def to_bytes(value: Any, type_str: str = "bytes32") -> bytes:

234

"""

235

Convert value to bytes.

236

237

Args:

238

value: Value to convert

239

type_str: Solidity bytes type (bytes1, bytes2, ..., bytes32, bytes)

240

241

Returns:

242

bytes: Converted bytes value

243

244

Raises:

245

ValueError: If value cannot be converted or is wrong length

246

"""

247

248

def to_bool(value: Any) -> bool:

249

"""

250

Convert value to boolean following Solidity rules.

251

252

Args:

253

value: Value to convert

254

255

Returns:

256

bool: Boolean value (0 is False, everything else is True)

257

"""

258

259

def to_string(value: Any) -> str:

260

"""

261

Convert value to string.

262

263

Args:

264

value: Value to convert

265

266

Returns:

267

str: String representation

268

"""

269

```

270

271

### Testing Framework Integration

272

273

Decorators and utilities for property-based testing with Hypothesis integration.

274

275

```python { .api }

276

def given(**strategies) -> Callable:

277

"""

278

Hypothesis property-based testing decorator.

279

280

Args:

281

**strategies: Mapping of parameter names to test strategies

282

283

Returns:

284

Callable: Decorated test function

285

286

Example:

287

@given(value=strategy('uint256'), sender=strategy('address'))

288

def test_transfer(token, value, sender):

289

# Test with generated values

290

"""

291

292

def strategy(name: str, **kwargs) -> Callable:

293

"""

294

Create custom test data generation strategy.

295

296

Args:

297

name: Strategy name (uint256, address, bytes32, etc.)

298

**kwargs: Strategy configuration options

299

300

Returns:

301

Callable: Strategy function for generating test data

302

303

Available strategies:

304

- address: Random Ethereum addresses

305

- uint256: Unsigned integers with configurable range

306

- int256: Signed integers with configurable range

307

- bytes32: Random 32-byte values

308

- string: Random strings with configurable length

309

- bool: Boolean values

310

- decimal: Fixed-point decimals

311

"""

312

313

def contract_strategy(name: str) -> Callable:

314

"""

315

Generate contract deployment strategies for testing.

316

317

Args:

318

name: Contract name to generate strategy for

319

320

Returns:

321

Callable: Strategy for contract deployment with random parameters

322

"""

323

```

324

325

### Testing Utilities

326

327

Additional utilities for smart contract testing including state management and assertion helpers.

328

329

```python { .api }

330

def reverts(reason: str = None) -> ContextManager:

331

"""

332

Context manager for testing transaction reverts.

333

334

Args:

335

reason: Expected revert reason string

336

337

Returns:

338

ContextManager: Context manager for revert testing

339

340

Example:

341

with reverts("Insufficient balance"):

342

token.transfer(recipient, amount, {'from': sender})

343

"""

344

345

def state_machine() -> Callable:

346

"""

347

Hypothesis stateful testing decorator for complex contract interactions.

348

349

Returns:

350

Callable: Decorator for stateful test classes

351

352

Example:

353

@state_machine()

354

class TokenStateMachine:

355

def __init__(self):

356

self.token = Token.deploy({'from': accounts[0]})

357

"""

358

```

359

360

## Usage Examples

361

362

### Wei and Unit Conversion

363

364

```python

365

from brownie.convert import Wei

366

367

# Create Wei values

368

amount1 = Wei(1000000000000000000) # 1 ether in wei

369

amount2 = Wei("1 ether") # Same as above

370

amount3 = Wei("20 gwei") # Gas price

371

amount4 = Wei(0.5) # 0.5 wei (float)

372

373

# Arithmetic operations

374

total = amount1 + amount2 # 2 ether

375

difference = amount1 - Wei("0.1 ether") # 0.9 ether

376

doubled = amount1 * 2 # 2 ether

377

half = amount1 / 2 # 0.5 ether

378

379

# Unit conversion

380

print(f"In ether: {amount1.to('ether')}") # 1.0

381

print(f"In gwei: {amount1.to('gwei')}") # 1000000000.0

382

print(f"In wei: {amount1.to('wei')}") # 1000000000000000000

383

384

# Comparisons

385

if amount1 > Wei("0.5 ether"):

386

print("Amount is greater than 0.5 ether")

387

388

# Use in transactions

389

account.transfer(recipient, Wei("1 ether"))

390

```

391

392

### Fixed-Point Decimals

393

394

```python

395

from brownie.convert import Fixed

396

397

# Create fixed-point numbers

398

price1 = Fixed("123.456789") # Full precision

399

price2 = Fixed(123.456789, 6) # 6 decimal places

400

price3 = Fixed(123456789, -6) # Scale factor

401

402

# Arithmetic with precision

403

total_price = price1 + price2

404

discount = total_price * Fixed("0.9") # 10% discount

405

final_price = total_price - discount

406

407

print(f"Final price: {final_price}")

408

409

# Use in contract calculations

410

token_amount = Fixed(user_input) * Fixed(exchange_rate)

411

contract.swap(int(token_amount), {'from': account})

412

```

413

414

### Address Handling

415

416

```python

417

from brownie.convert import to_address, EthAddress

418

419

# Convert various formats to address

420

addr1 = to_address("0x742d35Cc6634C0532925a3b8D8D944d0Cdbc1234")

421

addr2 = to_address(0x742d35Cc6634C0532925a3b8D8D944d0Cdbc1234)

422

addr3 = to_address(b'\x74\x2d\x35\xCc\x66\x34\xC0\x53\x29\x25\xa3\xb8\xD8\xD9\x44\xd0\xCd\xbc\x12\x34')

423

424

# Use EthAddress class

425

eth_addr = EthAddress("0x742d35cc6634c0532925a3b8d8d944d0cdbc1234")

426

print(f"Checksum address: {eth_addr}") # Proper checksum

427

print(f"Is checksum: {eth_addr.is_checksum()}")

428

429

# Address validation in functions

430

def validate_recipient(address):

431

try:

432

return to_address(address)

433

except ValueError:

434

raise ValueError("Invalid recipient address")

435

```

436

437

### Type Conversion

438

439

```python

440

from brownie.convert import to_uint, to_int, to_bytes, to_bool

441

442

# Convert to Solidity types

443

uint_value = to_uint(123, "uint8") # Fits in uint8

444

large_uint = to_uint("123456789", "uint256")

445

446

int_value = to_int(-123, "int16") # Signed integer

447

bytes_value = to_bytes("hello", "bytes32") # Padded to 32 bytes

448

bool_value = to_bool(1) # True

449

450

# Use in contract interactions

451

contract.setValues(

452

to_uint(user_amount, "uint256"),

453

to_bytes(user_data, "bytes32"),

454

to_bool(user_flag),

455

{'from': account}

456

)

457

458

# Handle conversion errors

459

try:

460

value = to_uint(300, "uint8") # Too large for uint8

461

except ValueError as e:

462

print(f"Conversion error: {e}")

463

```

464

465

### Return Value Handling

466

467

```python

468

from brownie.convert import ReturnValue

469

470

# Contract method with multiple returns

471

result = contract.getTokenInfo() # Returns (name, symbol, decimals, totalSupply)

472

473

# Access by index

474

token_name = result[0]

475

token_symbol = result[1]

476

477

# Access by name (if ABI available)

478

token_name = result['name']

479

decimals = result['decimals']

480

481

# Convert to dictionary

482

token_info = result.dict()

483

print(f"Token info: {token_info}")

484

485

# Iterate over values

486

for i, value in enumerate(result):

487

print(f"Return value {i}: {value}")

488

```

489

490

### Property-Based Testing

491

492

```python

493

from brownie.test import given, strategy

494

from brownie import accounts, reverts

495

496

@given(

497

amount=strategy('uint256', max_value=10**18),

498

recipient=strategy('address')

499

)

500

def test_transfer(token, amount, recipient):

501

"""Test token transfer with random values."""

502

sender = accounts[0]

503

initial_balance = token.balanceOf(sender)

504

505

if amount <= initial_balance:

506

# Should succeed

507

tx = token.transfer(recipient, amount, {'from': sender})

508

assert token.balanceOf(sender) == initial_balance - amount

509

assert token.balanceOf(recipient) >= amount

510

else:

511

# Should revert

512

with reverts():

513

token.transfer(recipient, amount, {'from': sender})

514

515

@given(

516

value=strategy('uint256', min_value=1, max_value=1000),

517

data=strategy('bytes32')

518

)

519

def test_contract_call(contract, value, data):

520

"""Test contract method with random parameters."""

521

initial_state = contract.getState()

522

523

tx = contract.processData(value, data, {'from': accounts[0]})

524

525

# Verify state changes

526

new_state = contract.getState()

527

assert new_state != initial_state

528

assert contract.lastValue() == value

529

assert contract.lastData() == data

530

531

# Custom strategies

532

from hypothesis import strategies as st

533

534

address_strategy = strategy(

535

'address',

536

exclude=['0x0000000000000000000000000000000000000000'] # Exclude zero address

537

)

538

539

amount_strategy = strategy(

540

'uint256',

541

min_value=1,

542

max_value=Wei("1000 ether").to('wei')

543

)

544

545

@given(

546

sender=address_strategy,

547

amount=amount_strategy

548

)

549

def test_with_custom_strategies(token, sender, amount):

550

# Test implementation

551

pass

552

```

553

554

### Stateful Testing

555

556

```python

557

from brownie.test import state_machine, given, strategy

558

from brownie import accounts

559

import hypothesis.strategies as st

560

561

@state_machine()

562

class TokenStateMachine:

563

"""Stateful testing for complex token interactions."""

564

565

def __init__(self):

566

self.token = Token.deploy("Test", "TST", 18, 1000000, {'from': accounts[0]})

567

self.balances = {accounts[0].address: 1000000}

568

569

@given(

570

recipient=strategy('address'),

571

amount=st.integers(min_value=1, max_value=1000)

572

)

573

def transfer(self, recipient, amount):

574

"""State transition: transfer tokens."""

575

sender = accounts[0]

576

577

if self.balances[sender.address] >= amount:

578

# Execute transfer

579

self.token.transfer(recipient, amount, {'from': sender})

580

581

# Update internal state

582

self.balances[sender.address] -= amount

583

self.balances[recipient] = self.balances.get(recipient, 0) + amount

584

585

# Verify contract state matches internal state

586

assert self.token.balanceOf(sender) == self.balances[sender.address]

587

assert self.token.balanceOf(recipient) == self.balances[recipient]

588

589

@given(amount=st.integers(min_value=1, max_value=1000))

590

def mint(self, amount):

591

"""State transition: mint new tokens."""

592

recipient = accounts[1]

593

594

self.token.mint(recipient, amount, {'from': accounts[0]})

595

596

# Update internal state

597

self.balances[recipient] = self.balances.get(recipient, 0) + amount

598

599

# Verify state

600

assert self.token.balanceOf(recipient) == self.balances[recipient]

601

602

# Run stateful tests

603

TestToken = TokenStateMachine.TestCase

604

```

605

606

### Error Testing

607

608

```python

609

from brownie import reverts, VirtualMachineError

610

611

def test_revert_conditions(token):

612

"""Test various revert conditions."""

613

sender = accounts[0]

614

recipient = accounts[1]

615

616

# Test with specific revert message

617

with reverts("ERC20: transfer amount exceeds balance"):

618

token.transfer(recipient, token.totalSupply() + 1, {'from': sender})

619

620

# Test any revert

621

with reverts():

622

token.transfer("0x0000000000000000000000000000000000000000", 1, {'from': sender})

623

624

# Test custom error types

625

with reverts(VirtualMachineError):

626

token.riskyFunction({'from': sender})

627

628

# Allow reverted transactions for analysis

629

tx = token.transfer(recipient, token.totalSupply() + 1,

630

{'from': sender, 'allow_revert': True})

631

632

assert tx.status == 0 # Transaction reverted

633

print(f"Revert reason: {tx.revert_msg}")

634

```

635

636

## Type Definitions

637

638

```python { .api }

639

# Type aliases for conversion and testing

640

ConvertibleValue = Union[int, float, str, bytes]

641

EthereumAddress = Union[str, EthAddress]

642

SolidityType = str # e.g., "uint256", "bytes32", "address"

643

TestStrategy = Callable[..., Any]

644

StateMachine = Callable[..., Any]

645

RevertContext = ContextManager[None]

646

```