Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions protocols/morpho/governance_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,28 @@ def _format_ts(ts: int) -> str:
return datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S")


def _format_countdown(ts: int) -> str:
"""Human-friendly time remaining until ``ts``, e.g. ``(3 days)``."""
seconds = ts - int(datetime.now().timestamp())
if seconds <= 0:
return "(now)"
days = seconds / 86400
if days >= 1:
n = round(days)
return f"({n} day{'s' if n != 1 else ''})"
hours = seconds / 3600
if hours >= 1:
n = round(hours)
return f"({n} hour{'s' if n != 1 else ''})"
n = max(round(seconds / 60), 1)
return f"({n} minute{'s' if n != 1 else ''})"


def _explorer_link(chain: Chain, tx_hash: str) -> str:
base = chain.explorer_url
if not base or not tx_hash:
return tx_hash
return f"[{tx_hash[:10]}…]({base}/tx/{tx_hash})"
return f"[{tx_hash}…]({base}/tx/{tx_hash})"


def _operation_label(snapshot: V2GovernanceSnapshot, pc: PendingConfig) -> str:
Expand Down Expand Up @@ -277,7 +294,7 @@ def _alert_pending_new(snapshot: V2GovernanceSnapshot, pc: PendingConfig, operat
f"⏳ V2 [{snapshot.name}]({get_vault_url(snapshot.address, snapshot.chain)}) "
f"on {snapshot.chain.name}\n"
f"📥 Submitted: {operation_label}\n"
f"⏰ Executable at: {_format_ts(pc.valid_at)}\n"
f"⏰ Executable at: {_format_ts(pc.valid_at)} {_format_countdown(pc.valid_at)}\n"
f"🔗 Tx: {_explorer_link(snapshot.chain, pc.tx_hash)}",
PROTOCOL,
)
Expand Down
8 changes: 5 additions & 3 deletions protocols/morpho/v2_decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,13 @@ def _format_args(sig: str, args: tuple[Any, ...], chain: Chain | None = None) ->
"""Render a decoded argument tuple per signature."""
name = _function_name(sig)

if name in ("addAdapter", "removeAdapter"):
return f"adapter {_format_address(args[0])}"
if name == "removeAdapter":
return ""
if name == "addAdapter":
return f"{_format_address(args[0])}"
if name == "setIsAllocator":
addr, flag = args
return f"allocator {_format_address(addr)} = {bool(flag)}"
return f"{_format_address(addr)} = {bool(flag)}"
if name in (
"setReceiveSharesGate",
"setSendSharesGate",
Expand Down
6 changes: 3 additions & 3 deletions tests/test_morpho_v2_decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,21 @@ def test_add_adapter(self):
data = _build("addAdapter(address)", ["address"], [A1])
self.assertEqual(
decode_submit(data),
f"addAdapter(adapter {Web3.to_checksum_address(A1)})",
f"addAdapter({Web3.to_checksum_address(A1)})",
)

def test_remove_adapter(self):
data = _build("removeAdapter(address)", ["address"], [A2])
self.assertEqual(
decode_submit(data),
f"removeAdapter(adapter {Web3.to_checksum_address(A2)})",
"removeAdapter()",
)

def test_set_is_allocator_grant(self):
data = _build("setIsAllocator(address,bool)", ["address", "bool"], [A1, True])
self.assertEqual(
decode_submit(data),
f"setIsAllocator(allocator {Web3.to_checksum_address(A1)} = True)",
f"setIsAllocator({Web3.to_checksum_address(A1)} = True)",
)

def test_set_is_allocator_revoke(self):
Expand Down