Web Access
HTTP Requests
Send data to external services:
def post_request():
url = "https://test-server.genlayer.com/body/echo"
response = gl.nondet.web.request(
url,
method='POST',
body={}
)
return response.status_code
status_code = gl.eq_principle.strict_eq(post_request)Web Rendering
Render web page and extract content:
def render_page():
url = "https://test-server.genlayer.com/static/genvm/hello.html"
# Render HTML content
html_content = gl.nondet.web.render(url, mode='html')
return html_content
page_html = gl.eq_principle.strict_eq(render_page)Screenshot Capture
Take screenshots of web pages:
def take_screenshot():
url = "https://test-server.genlayer.com/static/genvm/hello.html"
# Capture page as image
screenshot = gl.nondet.web.render(url, mode='screenshot')
return screenshot
image_data = gl.eq_principle.strict_eq(take_screenshot)Handling HTTP Errors
External APIs can return error responses. Consider checking the status code:
def fetch_data():
response = gl.nondet.web.request(api_url, method='GET')
if response.status_code >= 400 and response.status_code < 500:
raise gl.UserError(f"API returned client error: {response.status_code}")
elif response.status_code >= 500:
raise gl.UserError(f"API temporarily unavailable: {response.status_code}")
return json.loads(response.body.decode("utf-8"))Consensus-Friendly Web Requests
When using web data in non-deterministic blocks, remember that the leader and validators make independent requests. External APIs may return different data between calls — timestamps change, counts update, caches vary.
Extract Stable Fields
One approach is to return only the fields that won't change between calls:
def leader_fn():
res = gl.nondet.web.get(api_url)
data = json.loads(res.body.decode("utf-8"))
# Only return fields that are stable across requests
return {"id": data["id"], "login": data["login"], "status": data["status"]}
# NOT: follower_count, updated_at, online_statusDerive Status from Variable Data
When raw data may differ between leader and validator (e.g., CI check counts change), consider comparing a derived summary instead of the raw values:
def validator_fn(leaders_res: gl.vm.Result) -> bool:
validator_data = leader_fn()
def derive_status(checks):
if not checks:
return "pending"
for c in checks:
if c.get("conclusion") != "success":
return "failing"
return "success"
return derive_status(leaders_res.calldata) == derive_status(validator_data)See the Equivalence Principle page for more validation patterns.