Skip to content

ogpu.protocol

1:1 wrappers around the OGPU contract ABIs. This is the layer that actually talks to the chain — everything above it (ogpu.client, ogpu.agent, ogpu.events) eventually funnels here.

The protocol layer exposes two parallel API styles:

  1. Instance classes (Source, Task, Response, Provider, Master) — stateless live proxies bound to a single contract address. Clean for dashboards and any code that already holds a concept of "this task" or "this provider". Every method is a fresh RPC call; only self.address is persisted.
  2. Module functions (nexus, controller, terminal, vault) — function-style wrappers on the four singleton contracts. Use when you want to do a single call without constructing an instance, or when iterating many addresses.

Every write function takes a signer= keyword argument that accepts a hex private key string, an eth_account.LocalAccount, or None (env-var fallback via resolve_signer). Vault operations have no env-var fallback — they require explicit signer= to prevent accidental cross-role transactions.

Revert decoding is handled uniformly by TxExecutor: every known Solidity revert reason maps to a typed OGPUError subclass, and unknown reverts fall through to a generic TxRevertError(reason=...). See error handling for the full mapping.


Instance classes

Source

ogpu.protocol.source.Source

Source(address: str, chain: object = None)

Live, stateless proxy to an on-chain Source contract.

Holds only self.address and self.chain — no cached state. Every method is a fresh RPC call. This makes method semantics predictable (you always read current chain state) and instance construction cheap (no RPC at __init__).

Two construction modes:

  • Source(address) — lazy. No RPC. Use when you trust the address and want to defer validation until the first read.
  • Source.load(address) — eager. Runs a cheap get_status() probe and raises SourceNotFoundError if the address isn't a valid source contract.

Equality is address-based and case-insensitive. Hashing matches equality, so Source instances can be used as dict keys or in sets.

Example
from ogpu.protocol import Source

source = Source.load("0xSOURCE")
print(source.get_status())
# <SourceStatus.ACTIVE: 0>

print(source.get_client())
# '0xOWNER...'

for task in source.get_tasks():
    print(task.address, task.get_status())

Attributes:

Name Type Description
address str

Checksummed contract address.

chain

Optional chain binding (defaults to None — most code uses the globally-active chain from ChainConfig).

Construct a lazy Source instance (no RPC).

Parameters:

Name Type Description Default
address str

Source contract address. Checksummed automatically.

required
chain object

Optional chain binding for future multi-chain support.

None
load classmethod
load(address: str, chain: object = None) -> Source

Eager constructor — validate that the address is a Source contract.

Runs a cheap get_status() probe. If the probe reverts, the address is either not a contract or not a Source, and SourceNotFoundError is raised immediately.

Parameters:

Name Type Description Default
address str

Source contract address.

required
chain object

Optional chain binding.

None

Returns:

Type Description
Source

A Source instance bound to the given address.

Raises:

Type Description
SourceNotFoundError

If the probe call fails.

get_client
get_client() -> str

Return the address that owns (published) this source.

Only this address can call set_status, set_params, or inactivate.

Returns:

Type Description
str

Owner's checksummed address.

get_status
get_status() -> SourceStatus

Return the current lifecycle status of the source.

Returns:

Type Description
SourceStatus

SourceStatus.ACTIVE or SourceStatus.INACTIVE.

get_params
get_params() -> SourceParams

Fetch the full on-chain SourceParams tuple.

Returns the raw parameters stored on the contract — name, description, environment bitmask, min payment, etc. — without touching IPFS. For the human-readable metadata (including logoUrl and description), call get_metadata().

Returns:

Type Description
SourceParams

SourceParams dataclass with every field from the

SourceParams

contract.

get_metadata
get_metadata() -> dict[str, Any]

Fetch the off-chain SourceMetadata JSON from IPFS.

Makes two calls: one to read SourceParams.imageMetadataUrl from chain, and one HTTP GET to fetch the pointed-at JSON. Returns the parsed dict — typically matches the structure of the SourceMetadata dataclass (cpu, nvidia, amd, name, description, logoUrl).

Returns:

Type Description
dict[str, Any]

Parsed metadata dict.

Raises:

Type Description
IPFSFetchError

Network error, timeout, or invalid JSON.

IPFSGatewayError

Gateway returned non-200.

get_task_count
get_task_count() -> int

Return the total number of tasks ever published against this source.

Returns:

Type Description
int

Integer task count.

get_tasks
get_tasks(lower: int = 0, upper: int | None = None) -> list[Task]

Return the tasks published against this source, as Task instances.

Paginated — omit upper (or pass None) to fetch all tasks via internal chunking. Results are returned as live Task proxies ready for further reads.

Parameters:

Name Type Description Default
lower int

Start index (inclusive). Defaults to 0.

0
upper int | None

End index (exclusive). Defaults to all.

None

Returns:

Type Description
list[Task]

List of Task instances.

get_registrant_count
get_registrant_count() -> int

Return the number of providers currently registered to this source.

Returns:

Type Description
int

Integer registrant count.

get_registrants
get_registrants(lower: int = 0, upper: int | None = None) -> list[str]

Return the addresses of providers registered to this source.

Paginated — omit upper to fetch all.

Parameters:

Name Type Description Default
lower int

Start index (inclusive).

0
upper int | None

End index (exclusive). Defaults to all.

None

Returns:

Type Description
list[str]

List of provider addresses.

get_registrant_id
get_registrant_id(provider: str) -> int

Return the slot index of a provider in the registrant list.

Parameters:

Name Type Description Default
provider str

Provider address to look up.

required

Returns:

Type Description
int

The integer slot index, or 0 if the provider is not registered.

get_preferred_environment_of
get_preferred_environment_of(provider: str) -> Environment

Return the hardware environment a provider registered with.

Every Nexus.register call records which environment the provider registered for (CPU, NVIDIA, or AMD). This reader returns the choice as a typed Environment flag.

Parameters:

Name Type Description Default
provider str

Provider address.

required

Returns:

Type Description
Environment

The provider's preferred Environment.

set_status
set_status(status: SourceStatus, *, signer: Signer | None = None) -> Receipt

Set the source's status by calling Source.setStatus(uint8) directly.

Goes straight to the instance contract (owner route) rather than through Nexus.setSourceStatus (admin route). Must be called by the source's owner.

Most client code uses inactivate() for the common transition to INACTIVE; set_status is the lower-level primitive.

Parameters:

Name Type Description Default
status SourceStatus

The target SourceStatus.

required
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the status change.

Raises:

Type Description
NotSourceOwnerError

If the signer isn't the source's owner.

set_params
set_params(params: SourceParams, *, signer: Signer | None = None) -> Receipt

Update the source's parameters by calling Nexus.updateSource.

Goes through Nexus (rather than the source contract directly) so the SourceUpdated event fires. Must be called by the source's owner.

Typically you'd build a fresh SourceInfo and pass it through client.update_source — that wrapper builds the SourceParams for you. Use this instance method when you already have a SourceParams tuple.

Parameters:

Name Type Description Default
params SourceParams

The new SourceParams.

required
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the update.

Raises:

Type Description
NotSourceOwnerError

If the signer isn't the source's owner.

inactivate
inactivate(*, signer: Signer | None = None) -> Receipt

Inactivate the source by calling Nexus.inactivateSource.

One-way transition — there is no reactivation. After this call, publishing new tasks against the source reverts with SourceInactiveError. Existing tasks continue their natural lifecycle.

Parameters:

Name Type Description Default
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the inactivation.

Raises:

Type Description
NotSourceOwnerError

If the signer isn't the source's owner.

snapshot
snapshot() -> SourceSnapshot

Return a frozen capture of every non-paginated field.

Issues one RPC per view call in sequence (not parallel) and bundles the results into a SourceSnapshot dataclass. Omits paginated reads (get_tasks, get_registrants) and IPFS reads (get_metadata) — call those explicitly if you need them.

Use snapshot() for dashboards, logging, and atomic reads where you want all the fields to come from roughly the same block. For single-field reads, use the getter directly.

Returns:

Type Description
SourceSnapshot

SourceSnapshot with address, client, status, params,

SourceSnapshot

task_count, registrant_count.

Task

ogpu.protocol.task.Task

Task(address: str, chain: object = None)

Live, stateless proxy to an on-chain Task contract.

Holds only self.address and self.chain — no cached state. Every method is a fresh RPC call.

A task's lifecycle: NEWATTEMPTED (first attempt arrives) → RESPONDED (first response submitted) → FINALIZED (response confirmed or first-response auto-finalization). Alternative exits: CANCELED (client canceled before any attempts) or EXPIRED (expiryTime passed without resolution).

Two construction modes:

  • Task(address) — lazy, no RPC.
  • Task.load(address) — eager, probes get_status() and raises TaskNotFoundError on failure.

Equality is case-insensitive address comparison; hashing matches, so Task instances are usable as dict keys or in sets.

Example
from ogpu.protocol import Task

task = Task.load("0xTASK")
print(task.get_status())
# <TaskStatus.NEW: 0>

source = task.get_source()  # navigate back to the source
for attempter in task.get_attempters():
    print(attempter)

Attributes:

Name Type Description
address str

Checksummed contract address.

chain

Optional chain binding.

Construct a lazy Task instance (no RPC).

Parameters:

Name Type Description Default
address str

Task contract address. Checksummed automatically.

required
chain object

Optional chain binding.

None
load classmethod
load(address: str, chain: object = None) -> Task

Eager constructor — validate that the address is a Task contract.

Runs a cheap get_status() probe. If the probe reverts, raises TaskNotFoundError.

Parameters:

Name Type Description Default
address str

Task contract address.

required
chain object

Optional chain binding.

None

Returns:

Type Description
Task

A Task instance bound to the given address.

Raises:

Type Description
TaskNotFoundError

If the probe call fails.

get_source
get_source() -> Source

Navigate from this task back to its parent source.

Returns a fresh Source instance (no RPC beyond the one that reads the address). Useful for chaining reads: task.get_source().get_status().

Returns:

Type Description
Source

A Source instance bound to the source contract.

get_status
get_status() -> TaskStatus

Return the current lifecycle status of the task.

Returns:

Type Description
TaskStatus

One of TaskStatus.NEW, ATTEMPTED, RESPONDED,

TaskStatus

CANCELED, EXPIRED, FINALIZED.

get_params
get_params() -> TaskParams

Fetch the full on-chain TaskParams tuple.

Includes the source address, IPFS config URL, expiry time, and payment. For the actual task input (function name + data), use get_metadata() which follows the IPFS URL.

Returns:

Type Description
TaskParams

TaskParams dataclass with every field from the contract.

get_metadata
get_metadata() -> dict[str, Any]

Fetch the task's config JSON from IPFS.

Reads TaskParams.config (an IPFS URL) and fetches the pointed-at JSON body. The returned dict typically matches the structure of the TaskInput that was uploaded when the task was published — function_name, data, and any extra fields the client added.

Returns:

Type Description
dict[str, Any]

Parsed config dict.

Raises:

Type Description
IPFSFetchError / IPFSGatewayError

On fetch failure.

get_payment
get_payment() -> int

Return the payment offered by this task, in wei.

Returns:

Type Description
int

Payment amount in wei.

get_expiry_time
get_expiry_time() -> int

Return the task's expiry as a Unix timestamp.

After this time, attempts and responses revert. Nexus will mark the task EXPIRED on the next attempt.

Returns:

Type Description
int

Unix timestamp.

get_delivery_method
get_delivery_method() -> DeliveryMethod

Return the delivery method inherited from the source.

Tasks inherit their delivery method from the source at publish time — it cannot be changed after publication.

Returns:

Type Description
DeliveryMethod

DeliveryMethod.MANUAL_CONFIRMATION or

DeliveryMethod

DeliveryMethod.FIRST_RESPONSE.

get_attempt_count
get_attempt_count() -> int

Return the number of providers who've attempted this task.

Returns:

Type Description
int

Integer attempt count.

get_attempters
get_attempters(lower: int = 0, upper: int | None = None) -> list[str]

Return the addresses of providers who attempted this task.

Paginated — omit upper to fetch all.

Parameters:

Name Type Description Default
lower int

Start index (inclusive).

0
upper int | None

End index (exclusive). Defaults to all.

None

Returns:

Type Description
list[str]

List of provider addresses.

get_attempter_id
get_attempter_id(provider: str) -> int

Return the slot index of a provider in the attempter list.

Parameters:

Name Type Description Default
provider str

Provider address.

required

Returns:

Type Description
int

The integer slot index, or 0 if the provider hasn't attempted.

get_attempt_timestamps
get_attempt_timestamps(lower: int = 0, upper: int | None = None) -> list[int]

Return the timestamps at which attempts were submitted.

Each attempter has a corresponding timestamp, stored at the same index as in get_attempters. Useful for latency analysis or dispatching timeouts based on how long ago a provider claimed the task.

Parameters:

Name Type Description Default
lower int

Start index.

0
upper int | None

End index. Defaults to all.

None

Returns:

Type Description
list[int]

List of Unix timestamps.

get_response_of
get_response_of(provider: str) -> Response | None

Return the response submitted by a specific provider.

Parameters:

Name Type Description Default
provider str

Provider address to query.

required

Returns:

Type Description
Response | None

A Response instance, or None if the provider hasn't

Response | None

submitted a response for this task.

get_responses
get_responses(lower: int = 0, upper: int | None = None) -> list[Response]

Return all responses submitted for this task, as Response instances.

Paginated — omit upper to fetch all.

Parameters:

Name Type Description Default
lower int

Start index.

0
upper int | None

End index. Defaults to all.

None

Returns:

Type Description
list[Response]

List of Response instances.

get_confirmed_response
get_confirmed_response() -> Response | None

Return the confirmed response for this task, if any.

Chain-only replacement for the old HTTP-based get_confirmed_response(task_address) function. Iterates through get_responses() and returns the first one with is_confirmed() == True. Since at most one response per task can be confirmed, this is the winning response.

Returns:

Type Description
Response | None

A confirmed Response, or None if no response has

Response | None

been confirmed yet.

get_winning_provider
get_winning_provider() -> str | None

Return the address of the provider whose response was confirmed.

Returns:

Type Description
str | None

Provider address if the task has been finalized, otherwise

str | None

None.

is_already_submitted
is_already_submitted(hash_bytes: bytes) -> bool

Return whether a given response hash has already been submitted.

Used by providers to deduplicate response content — the contract tracks submitted hashes to prevent double-submission of the same payload from the same provider.

Parameters:

Name Type Description Default
hash_bytes bytes

32-byte hash of the response payload.

required

Returns:

Type Description
bool

True if a response with this hash has already been recorded.

cancel
cancel(*, signer: Signer | None = None) -> Receipt

Cancel the task by calling Controller.cancelTask.

Only works while the task is still in NEW state — once any provider has called attempt, cancel reverts with TaskAlreadyFinalizedError (or similar). Must be called by the task's client.

On success, the escrowed payment is released back to the client and the task transitions to CANCELED.

Parameters:

Name Type Description Default
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the cancellation.

Raises:

Type Description
NotTaskOwnerError

If the signer isn't the task's client.

TaskAlreadyFinalizedError

If the task is past NEW state.

snapshot
snapshot() -> TaskSnapshot

Return a frozen capture of every non-paginated field.

Issues one RPC per view call (sequential, not parallel) and bundles the results into a TaskSnapshot dataclass. Omits paginated fields (get_attempters, get_responses) and IPFS-fetching fields (get_metadata) — call those explicitly.

Returns:

Type Description
TaskSnapshot

TaskSnapshot with address, source, status, params,

TaskSnapshot

payment, expiry_time, delivery_method, attempt_count, and

TaskSnapshot

winning_provider.

Response

ogpu.protocol.response.Response

Response(address: str, chain: object = None)

Live, stateless proxy to an on-chain Response contract.

Responses are deployed by Nexus.submitResponse whenever a provider produces output for a task. The Response contract carries the provider's signature, the payment they claimed, and a data field holding a URL (usually IPFS) to the actual payload.

Lifecycle: SUBMITTEDCONFIRMED. The transition happens when the client calls confirm_response (MANUAL_CONFIRMATION delivery) or automatically on the first submit (FIRST_RESPONSE delivery).

Construction:

  • Response(address) — lazy, no RPC.
  • Response.load(address) — eager, probes get_status().

Attributes:

Name Type Description
address str

Checksummed contract address.

chain

Optional chain binding.

Construct a lazy Response instance (no RPC).

Parameters:

Name Type Description Default
address str

Response contract address.

required
chain object

Optional chain binding.

None
load classmethod
load(address: str, chain: object = None) -> Response

Eager constructor — validate that the address is a Response contract.

Runs a get_status() probe and raises ResponseNotFoundError on failure.

Parameters:

Name Type Description Default
address str

Response contract address.

required
chain object

Optional chain binding.

None

Returns:

Type Description
Response

A Response instance bound to the given address.

Raises:

Type Description
ResponseNotFoundError

If the probe fails.

get_task
get_task() -> Task

Navigate from this response back to its parent task.

Returns a fresh Task instance. Useful for chaining: response.get_task().get_status().

Returns:

Type Description
Task

A Task instance bound to the parent task's address.

get_params
get_params() -> ResponseParams

Fetch the full on-chain ResponseParams tuple.

Returns:

Type Description
ResponseParams

ResponseParams with task, provider, data URL, and payment.

get_data
get_data() -> str

Return the raw data field (usually an IPFS URL) as a string.

Cheap — just reads the params tuple. For the actual content behind the URL, use fetch_data().

Returns:

Type Description
str

The data URL string.

fetch_data
fetch_data() -> dict[str, Any]

Fetch the off-chain payload from the data URL and parse as JSON.

Performs one HTTP GET and returns the parsed dict. Only handles JSON bodies — if a provider produced binary output (model weights, images), use get_data() to get the URL and fetch it with your own client.

Follows the A2 naming rule: get_data() is cheap/local (reads a field), fetch_data() is network I/O.

Returns:

Type Description
dict[str, Any]

Parsed JSON payload as a dict.

Raises:

Type Description
IPFSFetchError

Network error or invalid JSON.

IPFSGatewayError

Gateway returned non-200.

Example
response = task.get_confirmed_response()
if response:
    payload = response.fetch_data()
    print(payload["result"])
get_status
get_status() -> ResponseStatus

Return the current status of the response.

Returns:

Type Description
ResponseStatus

ResponseStatus.SUBMITTED or ResponseStatus.CONFIRMED.

get_timestamp
get_timestamp() -> int

Return the Unix timestamp when the response was submitted.

Returns:

Type Description
int

Unix timestamp.

is_confirmed
is_confirmed() -> bool

Return whether this response has been confirmed by the client.

Returns True if the response is the winning response for its parent task. For FIRST_RESPONSE delivery this flips to True immediately on submission; for MANUAL_CONFIRMATION it requires a separate confirm_response call from the client.

Returns:

Type Description
bool

True if confirmed.

confirm
confirm(*, signer: Signer | None = None) -> Receipt

Confirm this response by calling Controller.confirmResponse.

Must be called by the parent task's client. On success:

  • This response transitions to CONFIRMED.
  • The parent task transitions to FINALIZED.
  • The escrowed payment is released from the vault to the response's provider.

Only meaningful for MANUAL_CONFIRMATION delivery — for FIRST_RESPONSE, confirmation happens automatically on submission.

Parameters:

Name Type Description Default
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the confirmation.

Raises:

Type Description
NotTaskOwnerError

Caller isn't the task's client.

ResponseAlreadyConfirmedError

Already confirmed.

snapshot
snapshot() -> ResponseSnapshot

Return a frozen capture of every field.

Issues a few RPCs in sequence and bundles the results into a ResponseSnapshot dataclass. Note that fetch_data() is NOT included — the snapshot is chain-only, fetching IPFS is still an explicit call.

Returns:

Type Description
ResponseSnapshot

ResponseSnapshot with address, task, params, data URL,

ResponseSnapshot

status, timestamp, confirmed.

Provider

ogpu.protocol.provider.Provider

Provider(address: str, chain: object = None)

Synthetic instance class wrapping provider-scoped operations.

Unlike Source / Task / Response, the Provider class is NOT backed by a single contract ABI — there is no "Provider" contract on-chain. Instead, Provider(address) is a convenience wrapper that composes calls across three contracts:

  • Terminal — identity, pairing, read-only base/live data lookups
  • Vault — balance, lockup, unbonding, earnings, eligibility
  • Nexus — source registrations and attempts

Every method delegates to a module-level protocol function, filling in self.address as the provider argument where appropriate. This gives you a cleaner API surface when your code holds a "provider" concept (dashboards, provisioning scripts, scheduler code) without needing to pass the address around repeatedly.

Provider-app responsibilities are not exposed here

Operations that produce live compute output or self-reported provider state — submit_response, set_base_data, set_live_data — are intentionally absent. Those must be called by the docker source itself running real compute, not by an SDK caller, otherwise providers could spoof responses or capacity claims from arbitrary scripts.

Lazy by default — Provider(address) does no RPC. Use Provider.load(address) if you want an eager existence check (Terminal.isProvider must return True).

Example
from ogpu.protocol import Provider

p = Provider.load("0xPROVIDER")
print(p.get_master())           # from Terminal
print(p.get_lockup())           # from Vault
print(p.is_eligible())          # from Vault
for source in p.get_registered_sources():  # from Nexus
    print(source.address)

snap = p.snapshot()             # batch capture across all three contracts

Attributes:

Name Type Description
address str

Checksummed provider address.

chain

Optional chain binding.

Construct a lazy Provider instance (no RPC).

Parameters:

Name Type Description Default
address str

Provider address. Checksummed automatically.

required
chain object

Optional chain binding.

None
load classmethod
load(address: str, chain: object = None) -> Provider

Eager constructor — validate that the address is a registered provider.

Runs Terminal.isProvider(address) and raises ProviderNotFoundError if it returns False.

Parameters:

Name Type Description Default
address str

Provider address.

required
chain object

Optional chain binding.

None

Returns:

Type Description
Provider

A Provider instance.

Raises:

Type Description
ProviderNotFoundError

If the address is not a registered provider.

get_master
get_master() -> str

Return the master this provider is paired with.

Delegates to Terminal.masterOf(self.address). Returns the zero address if the provider isn't paired.

get_base_data
get_base_data() -> str

Return the long-lived metadata URL for this provider.

Delegates to Terminal.baseDataOf(self.address). Typically an IPFS URL pointing at a JSON document describing the provider's hardware and capabilities.

get_live_data
get_live_data() -> str

Return the short-lived status URL for this provider.

Delegates to Terminal.liveDataOf(self.address). Typically refreshed more frequently than base data — current load, health, etc.

is_provider
is_provider() -> bool

Return whether this address is registered as a provider.

Delegates to Terminal.isProvider(self.address).

get_default_agent_disabled
get_default_agent_disabled() -> bool

Return whether the provider opted out of default agent delegation.

Delegates to Terminal.defaultAgentDisabled(self.address). When True, the built-in scheduling agent (The Order) no longer dispatches tasks to this provider automatically.

get_balance
get_balance() -> int

Return available (liquid) vault balance in wei.

Delegates to Vault.balanceOf(self.address).

get_lockup
get_lockup() -> int

Return locked (staked) amount in wei.

Delegates to Vault.lockupOf(self.address). This is what backs source registrations.

get_unbonding
get_unbonding() -> int

Return amount currently unbonding in wei.

Delegates to Vault.unbondingOf(self.address). Zero unless the provider has called unbond and hasn't yet claim'd.

get_unbonding_timestamp
get_unbonding_timestamp() -> int

Return the Unix timestamp when the current unbonding matures.

Delegates to Vault.unbondingTimestamp(self.address).

get_total_earnings
get_total_earnings() -> int

Return cumulative earnings in wei.

Delegates to Vault.totalEarningsOf(self.address).

get_frozen_payment
get_frozen_payment() -> int

Return the amount escrowed against pending attempts in wei.

Delegates to Vault.frozenPayment(self.address).

get_sanction
get_sanction() -> int

Return protocol sanction amount in wei.

Delegates to Vault.sanctionOf(self.address). Usually zero.

is_eligible
is_eligible() -> bool

Return whether the provider is eligible for vault operations.

Delegates to Vault.isEligible(self.address).

is_whitelisted
is_whitelisted() -> bool

Return whether the provider is on the vault whitelist.

Delegates to Vault.isWhitelisted(self.address).

announce_master
announce_master(master: str, *, signer: Signer | None = None) -> Receipt

Pair this provider with a master by calling Terminal.announceMaster.

Must be called by the provider's own key.

Parameters:

Name Type Description Default
master str

Master address to pair with.

required
signer Signer | None

Provider signer. Falls back to PROVIDER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the call.

set_default_agent_disabled
set_default_agent_disabled(value: bool, *, signer: Signer | None = None) -> Receipt

Opt this provider out of (or back into) default agent delegation.

Delegates to Terminal.setDefaultAgentDisabled. Must be called by the provider's own key.

Parameters:

Name Type Description Default
value bool

True to disable, False to enable.

required
signer Signer | None

Provider signer. Falls back to PROVIDER_PRIVATE_KEY.

None
register_to
register_to(source: str, env: int, *, signer: Signer | None = None) -> Receipt

Register this provider to a source.

Delegates to Nexus.register(source, self.address, env). Can be called by the provider directly, the master, or an authorized agent.

Parameters:

Name Type Description Default
source str

Source contract address to register to.

required
env int

Preferred environment bitmask (Environment.CPU etc.).

required
signer Signer | None

Provider/master/agent signer. Falls back to PROVIDER_PRIVATE_KEY.

None
unregister_from
unregister_from(source: str, *, signer: Signer | None = None) -> Receipt

Unregister this provider from a source.

Delegates to Nexus.unregister(source, self.address).

Parameters:

Name Type Description Default
source str

Source contract address.

required
signer Signer | None

Provider/master/agent signer.

None
attempt
attempt(task: str, suggested_payment: int, *, signer: Signer | None = None) -> Receipt

Submit an attempt on a task for this provider.

Delegates to Nexus.attempt(task, self.address, suggested_payment).

Parameters:

Name Type Description Default
task str

Task contract address.

required
suggested_payment int

Advisory payment amount in wei.

required
signer Signer | None

Provider/master/agent signer.

None
stake
stake(amount: int, *, signer: Signer) -> Receipt

Lock amount into this provider's vault lockup.

Convenience wrapper for vault.lock. signer must be explicit — vault operations do not fall back to env vars.

Parameters:

Name Type Description Default
amount int

Amount to lock in wei.

required
signer Signer

Required hex key or LocalAccount.

required
unstake
unstake(amount: int, *, signer: Signer) -> Receipt

Start unbonding amount from this provider's lockup.

Convenience wrapper for vault.unbond.

cancel_unbonding
cancel_unbonding(*, signer: Signer) -> Receipt

Abort any pending unbonding for this provider.

Convenience wrapper for vault.cancel_unbonding.

claim_earnings
claim_earnings(*, signer: Signer) -> Receipt

Claim matured unbonded amounts into the liquid balance.

Convenience wrapper for vault.claim.

deposit_to_vault
deposit_to_vault(amount: int, *, signer: Signer) -> Receipt

Deposit amount into this provider's vault balance.

Convenience wrapper for vault.deposit(self.address, amount). The signer can be the provider themselves or anyone funding the provider (e.g. the master).

withdraw_from_vault
withdraw_from_vault(amount: int, *, signer: Signer) -> Receipt

Withdraw amount from this provider's liquid vault balance.

Convenience wrapper for vault.withdraw. signer must hold the keys for self.address — vault withdraw uses msg.sender.

snapshot
snapshot() -> ProviderSnapshot

Return a frozen capture of Terminal + Vault state in one batch.

Issues one RPC per field (sequential) and bundles the results into a ProviderSnapshot. Omits paginated Nexus fields (get_registered_sources) — call those explicitly.

Returns:

Type Description
ProviderSnapshot

ProviderSnapshot with every terminal and vault field.

Master

ogpu.protocol.master.Master

Master(address: str, chain: object = None)

Synthetic instance class wrapping master-scoped operations.

Like Provider, the Master class is NOT backed by a single contract ABI — Master(address) composes calls across Terminal and Vault into a cleaner per-master API surface. Lighter than Provider because masters have a smaller operational surface:

  • Terminal — pairing with a provider, setting agents, removing providers/containers
  • Vault — balance, lockup, earnings, eligibility

Masters do not touch Nexus directly — provider-side scheduling operations (register, attempt) are provider-role calls, though masters can authorize an agent to drive them via set_agent + ogpu.agent. Compute-output operations like submit_response are not exposed in the SDK at all and must be produced by the docker source running real compute.

Lazy by default; use Master.load(address) for eager existence checking (Terminal.isMaster must return True).

Example
from ogpu.protocol import Master

m = Master.load("0xMASTER")
print(m.get_provider())        # paired provider
print(m.get_lockup())          # master's staked amount

# Master authorizes an agent to act on their behalf
m.set_agent("0xTHE_ORDER", True, signer=MASTER_KEY)

Attributes:

Name Type Description
address str

Checksummed master address.

chain

Optional chain binding.

Construct a lazy Master instance (no RPC).

Parameters:

Name Type Description Default
address str

Master address.

required
chain object

Optional chain binding.

None
load classmethod
load(address: str, chain: object = None) -> Master

Eager constructor — validate that the address is a registered master.

Runs Terminal.isMaster(address) and raises MasterNotFoundError if it returns False.

Parameters:

Name Type Description Default
address str

Master address.

required
chain object

Optional chain binding.

None

Returns:

Type Description
Master

A Master instance.

Raises:

Type Description
MasterNotFoundError

If the address is not a registered master.

get_provider
get_provider() -> str

Return the provider this master is paired with.

Delegates to Terminal.providerOf(self.address). Returns the zero address if unpaired.

is_master
is_master() -> bool

Return whether this address is registered as a master.

Delegates to Terminal.isMaster(self.address).

get_balance
get_balance() -> int

Return available vault balance in wei.

get_lockup
get_lockup() -> int

Return locked (staked) amount in wei.

get_unbonding
get_unbonding() -> int

Return amount currently unbonding in wei.

get_total_earnings
get_total_earnings() -> int

Return cumulative earnings in wei.

get_frozen_payment
get_frozen_payment() -> int

Return the amount escrowed against pending task work in wei.

is_eligible
is_eligible() -> bool

Return whether the master is eligible for vault operations.

is_whitelisted
is_whitelisted() -> bool

Return whether the master is on the vault whitelist.

announce_provider
announce_provider(provider: str, amount: int, *, signer: Signer | None = None) -> Receipt

Claim a provider by calling Terminal.announceProvider (payable).

First half of master/provider pairing. Sends amount as msg.value — gets credited to the provider's vault lockup. The pairing is completed when the provider subsequently calls announceMaster.

Parameters:

Name Type Description Default
provider str

Provider address being claimed.

required
amount int

Initial funding amount in wei.

required
signer Signer | None

Master signer. Falls back to MASTER_PRIVATE_KEY.

None
remove_provider
remove_provider(provider: str, *, signer: Signer | None = None) -> Receipt

Break the pairing with a provider by calling Terminal.removeProvider.

Parameters:

Name Type Description Default
provider str

Provider address to remove.

required
signer Signer | None

Master signer. Falls back to MASTER_PRIVATE_KEY.

None
remove_container
remove_container(provider: str, source: str, *, signer: Signer | None = None) -> Receipt

Signal a provider should stop running a source's container.

Delegates to Terminal.removeContainer. Doesn't actually stop the docker container — emits an event the Provider App watches and acts on.

Parameters:

Name Type Description Default
provider str

Provider whose container should stop.

required
source str

Source whose container should stop.

required
signer Signer | None

Master signer. Falls back to MASTER_PRIVATE_KEY.

None
set_agent
set_agent(agent: str, value: bool, *, signer: Signer | None = None) -> Receipt

Authorize (or revoke) an agent to sign on this master's behalf.

Delegates to Terminal.setAgent(agent, value). After value=True, the agent's key can sign Nexus operations (register, attempt, etc.) for providers managed by this master.

Parameters:

Name Type Description Default
agent str

Agent address.

required
value bool

True to authorize, False to revoke.

required
signer Signer | None

Master signer. Falls back to CLIENT_PRIVATE_KEY via the protocol-level set_agent — pass signer=MASTER_KEY explicitly to sign as a master.

None
stake
stake(amount: int, *, signer: Signer) -> Receipt

Lock amount into this master's vault lockup.

Convenience wrapper for vault.lock. signer must be explicit — vault operations do not fall back to env vars.

unstake
unstake(amount: int, *, signer: Signer) -> Receipt

Start unbonding amount from this master's lockup.

Convenience wrapper for vault.unbond.

cancel_unbonding
cancel_unbonding(*, signer: Signer) -> Receipt

Abort any pending unbonding for this master.

Convenience wrapper for vault.cancel_unbonding.

claim_earnings
claim_earnings(*, signer: Signer) -> Receipt

Claim matured unbonded amounts into the liquid balance.

Convenience wrapper for vault.claim.

deposit_to_vault
deposit_to_vault(amount: int, *, signer: Signer) -> Receipt

Deposit amount into this master's vault balance.

Convenience wrapper for vault.deposit(self.address, amount).

withdraw_from_vault
withdraw_from_vault(amount: int, *, signer: Signer) -> Receipt

Withdraw amount from this master's liquid vault balance.

Convenience wrapper for vault.withdraw. signer must hold the keys for self.address.

snapshot
snapshot() -> MasterSnapshot

Return a frozen capture of Terminal + Vault state in one batch.

Returns:

Type Description
MasterSnapshot

MasterSnapshot with address, paired provider, master

MasterSnapshot

status, and all vault balance fields.

Module-level functions

ogpu.protocol.nexus

Nexus contract — low-level wrappers.

The Nexus contract is the central registry for sources, tasks, and provider registrations. Every client-side publish operation, every provider-side task execution, and most status-changing flows run through Nexus.

This module is a thin 1:1 mirror of the NexusAbi function surface. You rarely call these functions directly — the ogpu.client wrappers (publish_source, update_source, inactivate_source), the ogpu.agent wrappers (register_to, attempt), and the instance classes (Source.set_params, Task.cancel) all delegate here.

publish_source

publish_source(params: SourceParams, *, signer: Signer | None = None) -> Receipt

Call Nexus.publishSource(params) to deploy a new source.

This is the low-level entry point. client.publish_source is the user-facing wrapper that builds the SourceParams from a SourceInfo (including IPFS upload of metadata) and then wraps the receipt in a Source instance.

Parameters:

Name Type Description Default
params SourceParams

Fully-built SourceParams with imageMetadataUrl already pointing at an IPFS-hosted SourceMetadata JSON.

required
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY env var.

None

Returns:

Type Description
Receipt

Receipt for the published source. Use extract_source_address

Receipt

to pull the new source's contract address out of the receipt's

Receipt

SourcePublished event log.

Raises:

Type Description
MissingSignerError

If no signer is available.

TxRevertError / other OGPUError

See TxExecutor.execute.

update_source

update_source(source_address: str, params: SourceParams, *, signer: Signer | None = None) -> Receipt

Call Nexus.updateSource(source, params) to change source parameters.

Goes through Nexus (rather than the source contract directly) so the SourceUpdated event fires. Must be called by the source's owner.

Parameters:

Name Type Description Default
source_address str

The source contract to update.

required
params SourceParams

The new SourceParams tuple. Typically rebuilt from a fresh SourceInfo via the client wrapper client.update_source.

required
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the update transaction.

Raises:

Type Description
NotSourceOwnerError

If the signer isn't the source's owner.

MissingSignerError

If no signer is available.

inactivate_source

inactivate_source(source_address: str, *, signer: Signer | None = None) -> Receipt

Call Nexus.inactivateSource(source) to close a source to new tasks.

One-way transition — there is no "re-activate". Existing tasks under the source continue their natural lifecycle but no new tasks can be published against it.

Parameters:

Name Type Description Default
source_address str

The source contract to inactivate.

required
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the inactivation.

Raises:

Type Description
NotSourceOwnerError

If the signer isn't the source's owner.

MissingSignerError

If no signer is available.

register

register(source: str, provider: str, env: int, *, signer: Signer | None = None) -> Receipt

Call Nexus.register(source, provider, preferredEnvironment).

Registers a provider to attempt tasks on a source. Can be called by:

  • The provider itself (passing their own address as provider)
  • The provider's master (who manages them)
  • An agent authorized by the master via Terminal.setAgent

The protocol's isAgentOf check in the contract authorizes agent-based calls. The signer's role resolution defaults to PROVIDER (reads PROVIDER_PRIVATE_KEY), but you can pass any authorized key explicitly via signer=.

Parameters:

Name Type Description Default
source str

Source contract address to register to.

required
provider str

Provider address being registered. Same as signer for self-registration; different for master/agent-driven flows.

required
env int

Preferred environment bitmask from Environment (1 = CPU, 2 = NVIDIA, 4 = AMD). Pass a single value — this call registers the provider for one environment at a time.

required
signer Signer | None

The signing key. Falls back to PROVIDER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the registration.

Raises:

Type Description
InsufficientLockupError

Provider doesn't hold enough lockup for this source's minAvailableLockup.

SourceInactiveError

Source is already inactivated.

unregister

unregister(source: str, provider: str, *, signer: Signer | None = None) -> Receipt

Call Nexus.unregister(source, provider).

Removes a provider from the source's registrant list. Follows the same authorization rules as register — self, master, or authorized agent can call.

Parameters:

Name Type Description Default
source str

Source contract address.

required
provider str

Provider address being unregistered.

required
signer Signer | None

The signing key. Falls back to PROVIDER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the unregistration.

attempt

attempt(task: str, provider: str, suggested_payment: int, *, signer: Signer | None = None) -> Receipt

Call Nexus.attempt(task, provider, suggestedPayment).

Records that a provider is attempting a task. The provider is committing to produce a response — the suggested payment field is an advisory number about what the provider expects to receive from the client's escrowed payment for the task.

Follows the same authorization rules as register. Sets the task's status to ATTEMPTED on first call if it was NEW. If the task has already expired, the transaction may mark it as EXPIRED instead.

Parameters:

Name Type Description Default
task str

Task contract address.

required
provider str

Provider address making the attempt.

required
suggested_payment int

Advisory payment amount in wei.

required
signer Signer | None

The signing key. Falls back to PROVIDER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the attempt.

Raises:

Type Description
TaskExpiredError

Task's expiryTime has passed.

TaskAlreadyFinalizedError

Task is already in terminal state.

ogpu.protocol.controller

Controller contract — low-level wrappers.

The Controller contract handles client-side task lifecycle operations: publishing new tasks, confirming responses, and canceling tasks. Its role is distinct from Nexus — Controller mediates payment escrow and task finalization, while Nexus is the registry and attempt coordinator.

You rarely call these functions directly — the ogpu.client wrappers (publish_task, confirm_response, cancel_task) build the params, resolve signers, and hand the result back as instance classes or typed receipts.

publish_task

publish_task(params: TaskParams, *, signer: Signer | None = None) -> Receipt

Call Controller.publishTask(params) to publish a new task.

The Controller escrows the payment from the caller, deploys a new Task contract, and emits TaskPublished from Nexus. The returned receipt carries that event log — decode it with extract_task_address to get the new task's address.

This is the low-level entry point. client.publish_task is the user-facing wrapper that builds the TaskParams from a TaskInfo (including IPFS upload of the config payload) and wraps the receipt in a Task instance.

Parameters:

Name Type Description Default
params TaskParams

Fully-built TaskParams with config already pointing at an IPFS-hosted TaskInput JSON.

required
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY env var.

None

Returns:

Type Description
Receipt

Receipt for the published task.

Raises:

Type Description
SourceInactiveError

If the target source is inactive.

InsufficientBalanceError

If the client doesn't have enough vault balance to cover the task's payment.

MissingSignerError

If no signer is available.

confirm_response

confirm_response(response_address: str, *, signer: Signer | None = None) -> Receipt

Call Controller.confirmResponse(response) to finalize a task.

Must be called by the task's client (or their authorized agent). Flips the response to CONFIRMED, the parent task to FINALIZED, and triggers payment release from the vault to the winning provider.

Only needed for sources using MANUAL_CONFIRMATION delivery — sources with FIRST_RESPONSE delivery finalize automatically on the first submitResponse.

Parameters:

Name Type Description Default
response_address str

Response contract address to confirm.

required
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the confirmation.

Raises:

Type Description
NotTaskOwnerError

Caller isn't the task's client.

ResponseAlreadyConfirmedError

Response is already confirmed.

ResponseNotFoundError

No Response contract at that address.

cancel_task

cancel_task(task_address: str, *, signer: Signer | None = None) -> Receipt

Call Controller.cancelTask(task) to cancel a task before any attempts.

Only works while the task is in NEW state — once any provider has called attempt, cancel reverts with TaskAlreadyFinalizedError (or similar). Must be called by the task's client.

Successfully canceling a task releases the escrowed payment back to the client and transitions the task to CANCELED.

Parameters:

Name Type Description Default
task_address str

Task contract to cancel.

required
signer Signer | None

Client signer. Falls back to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the cancellation.

Raises:

Type Description
NotTaskOwnerError

Caller isn't the task's client.

TaskAlreadyFinalizedError

Task is already past NEW state.

ogpu.protocol.terminal

Terminal contract — low-level wrappers.

The Terminal contract handles identity and delegation: master/provider pairing, agent authorization, and provider metadata (base and live data). It's the contract that answers "who is who?" on-chain.

This module mirrors the Terminal ABI 1:1. Higher-level wrappers live in ogpu.client.set_agent (client-side agent setup), Provider / Master instance classes (role-scoped operations), and ogpu.agent (scheduler-role delegation).

set_agent

set_agent(agent: str, value: bool, *, signer: Signer | None = None) -> Receipt

Call Terminal.setAgent(agent, value) to authorize or revoke an agent.

Only clients and masters can set agents. Providers cannot — the Terminal.AgentSet event is only triggered by client or master signers (see PRD N1 for the protocol rationale).

Once authorized (value=True), the agent's own key can sign Nexus operations on behalf of the principal — the contract checks isAgentOf(principal, msg.sender) to authorize calls. Revoke by calling again with value=False, or use the revoke_agent one-liner.

The default env-var fallback is CLIENT_PRIVATE_KEY — pass signer= with a master key to set an agent for a master instead.

Parameters:

Name Type Description Default
agent str

Address to authorize/revoke as an agent.

required
value bool

True to authorize, False to revoke.

required
signer Signer | None

Client or master signer. Defaults to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the setAgent call.

Raises:

Type Description
InvalidSignerError

If agent is not a valid Ethereum address.

MissingSignerError

If no signer is available.

Example
from ogpu.protocol import terminal

# Client authorizes an agent to sign on their behalf
terminal.set_agent("0xAGENT", True, signer=CLIENT_KEY)

# Master authorizes the scheduling agent (The Order)
terminal.set_agent(
    "0x306Dc3fF30254675B209D916475094401aCC4a1E",
    True,
    signer=MASTER_KEY,
)

revoke_agent

revoke_agent(agent: str, *, signer: Signer | None = None) -> Receipt

Revoke an agent's authorization.

Shorthand for set_agent(agent, False, signer=...). Follows the same role/authorization rules — only the principal that authorized the agent can revoke.

Parameters:

Name Type Description Default
agent str

Address to revoke.

required
signer Signer | None

Client or master signer. Defaults to CLIENT_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the revocation.

announce_master

announce_master(master_address: str, *, signer: Signer | None = None) -> Receipt

Call Terminal.announceMaster(master). Provider-role caller.

Second half of the master/provider pairing handshake. After the master has called announceProvider to claim a provider, the provider calls announceMaster to confirm the link from their side. Both calls must succeed for masterOf(provider) to resolve.

Parameters:

Name Type Description Default
master_address str

Master address the provider is pairing with.

required
signer Signer | None

Provider signer. Falls back to PROVIDER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the announceMaster call.

announce_provider

announce_provider(provider: str, amount: int, *, signer: Signer | None = None) -> Receipt

Call Terminal.announceProvider(provider, amount). Master-role caller, payable.

First half of the master/provider pairing handshake. The master claims a provider and sends an initial funding amount in the same transaction — the amount gets credited to the provider's vault lockup so they become immediately eligible to register to sources.

Pairing is completed when the provider subsequently calls announceMaster(master).

Parameters:

Name Type Description Default
provider str

Provider address being announced.

required
amount int

Initial lockup amount in wei. Sent as msg.value in the transaction. Pass 0 if the provider already has enough lockup from prior deposits.

required
signer Signer | None

Master signer. Falls back to MASTER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the announceProvider call.

remove_provider

remove_provider(provider: str, *, signer: Signer | None = None) -> Receipt

Call Terminal.removeProvider(provider). Master-role caller.

Breaks the pairing between a master and a provider. After the call, masterOf(provider) no longer resolves and the provider is no longer considered managed by that master.

Parameters:

Name Type Description Default
provider str

Provider address to remove.

required
signer Signer | None

Master signer. Falls back to MASTER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the remove call.

remove_container

remove_container(provider: str, source: str, *, signer: Signer | None = None) -> Receipt

Call Terminal.removeContainer(provider, source). Master-role caller.

Signals that a provider should stop running the source's container. Used by the Provider App as a management signal — the container itself is not uninstalled by this call; the Provider App reads the RemoveContainer event and tears down the docker instance.

Parameters:

Name Type Description Default
provider str

Provider whose container should stop.

required
source str

Source whose container should stop.

required
signer Signer | None

Master signer. Falls back to MASTER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the remove call.

set_default_agent_disabled

set_default_agent_disabled(value: bool, *, signer: Signer | None = None) -> Receipt

Call Terminal.setDefaultAgentDisabled(value). Provider-role caller.

Opts a provider out of (or back into) default agent delegation. When disabled, the default scheduling agent (The Order) no longer dispatches tasks to this provider automatically — the provider becomes invisible to the built-in scheduler and has to be dispatched manually.

Like the base/live data setters, this uses msg.sender to identify the provider.

Parameters:

Name Type Description Default
value bool

True to disable the default agent, False to re-enable it.

required
signer Signer | None

Provider signer. Falls back to PROVIDER_PRIVATE_KEY.

None

Returns:

Type Description
Receipt

Receipt for the setDefaultAgentDisabled call.

get_master_of

get_master_of(provider: str) -> str

Return the master paired with a provider.

Returns the zero address if the provider is not currently paired with any master.

Parameters:

Name Type Description Default
provider str

Provider address to look up.

required

Returns:

Type Description
str

The master's checksummed address, or the zero address if unpaired.

get_provider_of

get_provider_of(master: str) -> str

Return the provider paired with a master.

The inverse lookup of get_master_of. Returns the zero address if the master has not paired with any provider.

Parameters:

Name Type Description Default
master str

Master address to look up.

required

Returns:

Type Description
str

The provider's checksummed address, or the zero address if unpaired.

get_base_data_of

get_base_data_of(provider: str) -> str

Return the current base data URL for a provider.

Parameters:

Name Type Description Default
provider str

Provider address to look up.

required

Returns:

Type Description
str

The base data URL as stored on-chain, or an empty string if

str

never set.

get_live_data_of

get_live_data_of(provider: str) -> str

Return the current live data URL for a provider.

Parameters:

Name Type Description Default
provider str

Provider address to look up.

required

Returns:

Type Description
str

The live data URL as stored on-chain, or an empty string if

str

never set.

is_master

is_master(address: str) -> bool

Return whether an address is registered as a master.

Parameters:

Name Type Description Default
address str

Address to check.

required

Returns:

Type Description
bool

True if the address has been announced as a master by at least

bool

one provider.

is_provider

is_provider(address: str) -> bool

Return whether an address is registered as a provider.

Parameters:

Name Type Description Default
address str

Address to check.

required

Returns:

Type Description
bool

True if the address has been announced as a provider by at

bool

least one master.

is_agent_of

is_agent_of(account: str, agent: str) -> bool

Return whether agent is authorized to sign on behalf of account.

Parameters:

Name Type Description Default
account str

The principal (client or master address).

required
agent str

The candidate agent address.

required

Returns:

Type Description
bool

True if account has called set_agent(agent, True).

is_default_agent_disabled

is_default_agent_disabled(address: str) -> bool

Return whether the provider opted out of default agent delegation.

Parameters:

Name Type Description Default
address str

Provider address to check.

required

Returns:

Type Description
bool

True if the provider has disabled the default scheduling agent.

ogpu.protocol.vault

Vault contract — low-level wrappers.

The Vault contract holds all staked tokens: provider lockups that back source registration, client deposits that escrow task payment, and master funds that back their managed providers. Every role in the protocol eventually interacts with the vault to deposit, lock, unbond, or claim.

All write functions require signer= as an explicit keyword argument. There is no env-var fallback — this is intentional (per decision C1-vault in the PRD). Vault operations are role-agnostic (any account can hold a balance), and falling back to a role-specific env var invites accidents when multiple roles share a process. Always pass signer= explicitly.

deposit

deposit(account: str, amount: int, *, signer: Signer) -> Receipt

Call Vault.deposit(account) to credit amount to account.

The Solidity function is payable — account is the beneficiary address being credited, amount is sent as msg.value. The signer pays gas and funds the deposit from their own balance; account receives the credit.

Typically signer and account are the same (you deposit for yourself) but they don't have to be — a master can deposit on behalf of a managed provider, and Provider.deposit_to_vault uses this pattern to pre-fill account=self.address.

Parameters:

Name Type Description Default
account str

The address being credited (beneficiary).

required
amount int

Amount to deposit in wei. Must match what the signer is willing to send as msg.value.

required
signer Signer

Required. Hex key or LocalAccount.

required

Returns:

Type Description
Receipt

Receipt for the deposit.

Raises:

Type Description
MissingSignerError

If no signer is passed (vault calls do not fall back to env vars).

Example
from web3 import Web3
from ogpu.protocol import vault
vault.deposit(
    "0xBENEFICIARY",
    amount=Web3.to_wei(1, "ether"),
    signer=MY_KEY,
)

withdraw

withdraw(amount: int, *, signer: Signer) -> Receipt

Call Vault.withdraw(amount) to pull funds out of the signer's balance.

Only works on the unlocked portion of the signer's vault balance — locked or unbonding funds cannot be withdrawn until they return to the liquid balance via claim.

Parameters:

Name Type Description Default
amount int

Amount to withdraw in wei.

required
signer Signer

Required. Hex key or LocalAccount.

required

Returns:

Type Description
Receipt

Receipt for the withdraw.

Raises:

Type Description
InsufficientBalanceError

If the signer's unlocked balance is less than amount.

lock

lock(amount: int, *, signer: Signer) -> Receipt

Call Vault.lock(amount) to stake amount from the liquid balance.

Moves funds from the signer's liquid (available) balance into the locked (staked) portion. Locked tokens are what make a provider eligible to register to sources — Vault.minLockupPerSource is checked on every Nexus.register call.

Parameters:

Name Type Description Default
amount int

Amount to lock in wei.

required
signer Signer

Required. Hex key or LocalAccount.

required

Returns:

Type Description
Receipt

Receipt for the lock.

Raises:

Type Description
InsufficientBalanceError

If the signer's liquid balance is less than amount.

unbond

unbond(amount: int, *, signer: Signer) -> Receipt

Call Vault.unbond(amount) to start unbonding amount of locked funds.

Begins the unbonding cooldown. Tokens move from the locked balance into the unbonding bucket and stay there for Vault.UNBONDING_PERIOD seconds. After the period elapses, claim moves them to the liquid balance where they can be withdrawn.

Parameters:

Name Type Description Default
amount int

Amount to begin unbonding, in wei.

required
signer Signer

Required. Hex key or LocalAccount.

required

Returns:

Type Description
Receipt

Receipt for the unbond.

Raises:

Type Description
InsufficientLockupError

If the signer's locked balance is less than amount.

cancel_unbonding

cancel_unbonding(*, signer: Signer) -> Receipt

Call Vault.cancelUnbonding() to abort a pending unbonding.

Moves any in-flight unbonding amount back to the locked balance. Useful when you change your mind before the cooldown elapses.

Parameters:

Name Type Description Default
signer Signer

Required. Hex key or LocalAccount.

required

Returns:

Type Description
Receipt

Receipt for the cancellation.

claim

claim(*, signer: Signer) -> Receipt

Call Vault.claim() to move matured unbonding back to the liquid balance.

Only works after the unbonding period has fully elapsed (check get_unbonding_timestamp vs. current time). After a successful claim, the matured amount is available for withdraw.

Parameters:

Name Type Description Default
signer Signer

Required. Hex key or LocalAccount.

required

Returns:

Type Description
Receipt

Receipt for the claim.

Raises:

Type Description
UnbondingPeriodNotElapsedError

If the cooldown hasn't finished.

get_balance_of

get_balance_of(address: str) -> int

Return the available (liquid) balance for an address, in wei.

The portion of the vault deposit that isn't locked, unbonding, or escrowed in a frozen payment. This is what can be withdrawn.

Parameters:

Name Type Description Default
address str

Account to query.

required

Returns:

Type Description
int

Available balance in wei.

get_lockup_of

get_lockup_of(address: str) -> int

Return the locked (staked) amount for an address, in wei.

The portion backing source registrations. Can be moved to unbonding via unbond.

Parameters:

Name Type Description Default
address str

Account to query.

required

Returns:

Type Description
int

Locked amount in wei.

get_unbonding_of

get_unbonding_of(address: str) -> int

Return the amount currently unbonding for an address, in wei.

Tokens in the cooldown window between unbond and claim. Zero once claim has been called for all matured unbondings.

Parameters:

Name Type Description Default
address str

Account to query.

required

Returns:

Type Description
int

Unbonding amount in wei.

get_unbonding_timestamp

get_unbonding_timestamp(address: str) -> int

Return the unix timestamp when the current unbonding matures.

After this time, claim will succeed. Zero if no unbonding is in progress.

Parameters:

Name Type Description Default
address str

Account to query.

required

Returns:

Type Description
int

Unix timestamp.

get_total_earnings_of

get_total_earnings_of(address: str) -> int

Return cumulative earnings for an address, in wei.

Monotonically-increasing counter of everything earned through completed tasks, regardless of whether it's still in the vault.

Parameters:

Name Type Description Default
address str

Account to query.

required

Returns:

Type Description
int

Cumulative earnings in wei.

get_frozen_payment

get_frozen_payment(address: str) -> int

Return the amount escrowed against pending task work, in wei.

Funds earmarked for pending attempts that haven't been confirmed or refunded yet. Not available for withdraw.

Parameters:

Name Type Description Default
address str

Account to query.

required

Returns:

Type Description
int

Frozen payment in wei.

get_sanction_of

get_sanction_of(address: str) -> int

Return the sanction amount for an address, in wei.

Protocol-level sanctions applied for misbehavior. Usually zero; non-zero values indicate the account has been penalized.

Parameters:

Name Type Description Default
address str

Account to query.

required

Returns:

Type Description
int

Sanction amount in wei.

is_eligible

is_eligible(address: str) -> bool

Return whether an account is eligible for vault operations.

Accounts that fail this check cannot participate in protocol flows (registration, attempts, etc.). Usually a combination of lockup thresholds, whitelist status, and sanction state.

Parameters:

Name Type Description Default
address str

Account to check.

required

Returns:

Type Description
bool

True if eligible.

is_whitelisted

is_whitelisted(address: str) -> bool

Return whether an account is on the vault whitelist.

Used for protocol-level access control — whitelisted accounts may get preferential eligibility or sanction handling.

Parameters:

Name Type Description Default
address str

Account to check.

required

Returns:

Type Description
bool

True if whitelisted.

get_min_lockup_per_source

get_min_lockup_per_source() -> int

Return Vault.MIN_LOCKUP_PER_SOURCE in wei.

The minimum lockup a provider must hold in the vault to register to any source. Sources can enforce a higher minimum via their own minAvailableLockup field, but this is the protocol-wide floor.

Returns:

Type Description
int

Minimum lockup amount in wei.

get_unbonding_period

get_unbonding_period() -> int

Return Vault.UNBONDING_PERIOD in seconds.

The cooldown window between unbond and claim. After calling unbond, you must wait at least this many seconds before you can claim.

Returns:

Type Description
int

Unbonding period in seconds.

Shared infrastructure

ogpu.protocol.TxExecutor

TxExecutor(contract: Contract, function_name: str, args: tuple[Any, ...] = (), *, signer: LocalAccount, value: int = 0, context: str | None = None, max_retries: int = 3)

Single-shot transaction sender with retry, nonce, and revert decoding.

Every write operation in the SDK — publish_source, publish_task, confirm_response, set_agent, vault.lock, etc. — constructs a TxExecutor and calls .execute(). The executor handles:

  • Nonce resolution via NonceManager (with automatic retry on nonce collisions)
  • Transaction building via contract.functions.X.build_transaction
  • Signing with the provided LocalAccount
  • Broadcasting via eth.send_raw_transaction
  • Confirmation via wait_for_transaction_receipt
  • Retry on nonce too low / replacement transaction underpriced errors, up to max_retries attempts
  • Revert decodingContractLogicError is passed through decode_revert and re-raised as a typed OGPUError subclass
  • Receipt wrapping — returns a frozen Receipt dataclass instead of the raw web3 AttributeDict

You usually don't construct this class directly — use the module-level functions (nexus.publish_source, controller.cancel_task, etc.) or instance methods (task.cancel(), source.inactivate()) and let them handle it.

Attributes:

Name Type Description
contract

The web3.contract.Contract to call into.

function_name

Name of the contract function to invoke.

args

Positional arguments to pass to the function.

signer

LocalAccount used to sign the transaction.

value

ETH value (wei) to attach to the call. Only non-zero for payable functions (Vault.deposit, Terminal.announceProvider).

context

Free-form string used in revert decoding for disambiguating errors. Defaults to "{contract.address}.{function_name}".

max_retries

Maximum retry attempts on recoverable errors (nonce/underpriced). Defaults to 3.

Example
from ogpu.protocol import TxExecutor, load_contract
from eth_account import Account

signer = Account.from_key("0x...")
contract = load_contract("ControllerAbi")
receipt = TxExecutor(
    contract,
    "cancelTask",
    ("0xTASK",),
    signer=signer,
).execute()
print(receipt.tx_hash)
# '0x...'

execute

execute() -> Receipt

Build, sign, broadcast, and wait for the transaction.

Runs the full retry/revert/receipt pipeline. On success returns a Receipt with the tx hash, block number, gas used, and decoded log list. On failure raises a typed OGPUError subclass — never a raw ContractLogicError or web3 exception.

Returns:

Type Description
Receipt

A Receipt dataclass for the mined transaction.

Raises:

Type Description
TxRevertError

Contract reverted with a reason not in REVERT_PATTERN_MAP, or mined with status == 0.

PermissionError

Contract reverted with a known permission check (NotTaskOwnerError, NotSourceOwnerError, NotMasterError, NotProviderError, etc.).

StateError

Contract reverted in a state that doesn't allow the operation (TaskExpiredError, TaskAlreadyFinalizedError, ResponseAlreadyConfirmedError, SourceInactiveError).

VaultError

On-chain vault check failed (InsufficientBalanceError, InsufficientLockupError, UnbondingPeriodNotElapsedError, NotEligibleError).

NonceError

Nonce collision exhausted all retries.

GasError

Underpriced transaction couldn't be recovered.

ogpu.protocol.resolve_signer

resolve_signer(signer: Signer | None, role: Role | None = None) -> LocalAccount

Normalize a signer argument to a LocalAccount.

The entry point every write operation in the SDK uses to turn a user-provided signer into a concrete account the transaction layer can sign with. Handles three cases:

  1. Already a LocalAccount — returned unchanged. This is the path hardware wallets, KMS, and other external signer backends take: construct a LocalAccount-shaped object once, pass it in as signer=.
  2. Hex string — parsed via Account.from_key. Most common path.
  3. None — fall back to the role-specific env var. If role is also None (vault operations), raise MissingSignerError immediately.

Parameters:

Name Type Description Default
signer Signer | None

The user-provided signer, or None to trigger the env-var fallback.

required
role Role | None

Which role the operation is acting as — controls which env var to read when signer is None. Pass None to disable the fallback entirely (vault convention).

None

Returns:

Type Description
LocalAccount

A LocalAccount ready to sign transactions.

Raises:

Type Description
MissingSignerError

If signer is None and either no role is set or the role's env var is unset.

InvalidSignerError

If signer is a string that isn't a valid private key, or a type the resolver doesn't know how to handle, or the env var contains an invalid key.

Example
import os
from eth_account import Account
from ogpu.protocol._signer import resolve_signer
from ogpu.types import Role

# Explicit hex string
acc = resolve_signer("0x" + "11" * 32, role=Role.CLIENT)
print(acc.address)
# '0x...'

# Pre-built LocalAccount (e.g. from a hardware wallet)
local = Account.from_key("0x" + "11" * 32)
resolve_signer(local, role=Role.CLIENT) is local
# True

# Env var fallback — reads CLIENT_PRIVATE_KEY
os.environ["CLIENT_PRIVATE_KEY"] = "0x" + "11" * 32
acc = resolve_signer(None, role=Role.CLIENT)

# Vault-style: no fallback allowed
resolve_signer(None, role=None)  # raises MissingSignerError

ogpu.protocol.load_contract

load_contract(abi_name: str, address: str | None = None) -> Contract

Load a web3.contract.Contract for an ABI + address.

Two modes:

  1. Singleton resolution (address=None) — for the four protocol-level singletons (Nexus, Controller, Terminal, Vault), looks up the address from ChainConfig.CHAIN_CONTRACTS for the current chain and wraps it with the matching ABI.
  2. Explicit address — for instance-bound contracts (Source, Task, Response), pass the deployed contract address directly.

The ABI is loaded via ChainConfig.load_abi which caches the parsed JSON per chain, so subsequent calls are cheap.

Parameters:

Name Type Description Default
abi_name str

ABI file basename without extension. Known singleton ABIs: "NexusAbi", "ControllerAbi", "TerminalAbi", "VaultAbi". Known instance ABIs: "SourceAbi", "TaskAbi", "ResponseAbi".

required
address str | None

Contract address. Omit for singleton ABIs.

None

Returns:

Type Description
Contract

A web3.contract.Contract bound to the resolved address and

Contract

the requested ABI.

Raises:

Type Description
ValueError

If abi_name is an instance ABI and no address was given.

FileNotFoundError

If the ABI file doesn't exist for the current chain.

Example
# Singleton
nexus = load_contract("NexusAbi")

# Instance
task = load_contract("TaskAbi", address="0x...")