Exercise: add a second event, and read both from one transaction#
← Back to Week 4: Track exposure
Same rules as 01-extend-the-contract.md: short, optional, no answer key, done on a copy of ../project/contracts/Greeter.vy or directly on it. Greeter.vy already declares and emits one event, GreetingChanged, every time set_greeting runs. This exercise adds a second, different event, emitted under its own condition, and then has you read both of them back out of a single transaction's receipt from Python, something none of the code you've been given so far actually does.
The task#
Add a new event that flags whether a greeting is "long," using a length threshold you choose (20 characters is a reasonable default), and emit it every time set_greeting runs, right alongside the existing GreetingChanged event, not instead of it. A single transaction can emit more than one event; there's nothing unusual about that.
event GreetingLengthFlag:
length: uint256
is_long: bool
Add that block near the top of the contract, next to the existing GreetingChanged declaration. Then, inside set_greeting, after the existing log GreetingChanged(...) line, add:
is_long: bool = len(new_greeting) > 20
log GreetingLengthFlag(length=len(new_greeting), is_long=is_long)
len(new_greeting) is how you get a string's length in Vyper, and it works directly on a String[N] value; Vyper doesn't need Solidity's extra bytes(newGreeting).length conversion step, len() already returns the length in bytes, the same measurement, without a conversion to write first. (This measures length in bytes, not characters; for plain English text without accented letters or emoji, those are the same number, which is close enough for what this exercise is teaching. If you're curious why they can differ for other text, that's a real detail of how UTF-8 encoding works, not a Vyper quirk, and worth a search later, but not something to chase down right now.)
Recompile and redeploy#
ape compile
ape run deploy --network ethereum:sepolia:<your-rpc-url>
ape run get_abi --network ethereum:sepolia:<your-rpc-url>
New bytecode means a new address, and the ABI needs regenerating so web3.py knows GreetingLengthFlag exists at all; without an up-to-date ABI, web3.py has no way to decode a log it doesn't know the shape of. Update CONTRACT_ADDRESS in your Python script to the freshly deployed address.
Read both events back out of one receipt#
interact.py, as given, never actually looks at events at all, even though Greeter.vy has been emitting GreetingChanged this whole time; it only ever calls get_greeting() directly to check the result. This exercise is your first time actually reading one back.
Send a set_greeting transaction the same way interact.py already does (build_transaction, sign_transaction, send_raw_transaction), and keep the receipt object wait_for_transaction_receipt hands back, the same way the existing script already does to read receipt.blockNumber. A receipt carries every event any contract emitted during that one transaction, but in raw, encoded form; you need the contract's own event objects, built from its ABI, to decode them back into readable Python values:
changed_events = greeter.events.GreetingChanged().process_receipt(receipt)
flag_events = greeter.events.GreetingLengthFlag().process_receipt(receipt)
for event in changed_events:
print("GreetingChanged:", event.args.old_greeting, "->", event.args.new_greeting)
for event in flag_events:
print("GreetingLengthFlag:", event.args.length, "long?", event.args.is_long)
Each call to process_receipt returns a list, because in general a single transaction could emit the same event type more than once (this one doesn't, but the API is written for the general case). Each entry's .args gives you the actual decoded values, named exactly like the fields you declared in the event, snake_case here because that's what the ABI says, generated straight from Vyper's own field names.
Send a short greeting (under your threshold) and a long one (over it) as two separate set_greeting calls, and confirm is_long comes back False for the first and True for the second. If process_receipt comes back empty for an event you know fired, double-check you're calling it on the freshly redeployed contract's ABI and address, not on an older greeter_abi.json left over from before you added the new event; a stale ABI simply doesn't know the event exists, so it silently decodes nothing, rather than raising an error that would point you at the actual problem.