Infernet
SDK
Patterns
Delegator

Delegator

Technical Reference

By default, Subscriptions are limited to:

On-chain initiation, on-chain fulfillment

  1. Originating on-chain via the Coordinator's createSubscription() function or inherited consumers
  2. Fulfillment on-chain via the Coordinator's deliverCompute() function

But, this severely limits valid use-cases where a developer may want to initiate a subscription entirely off-chain, submitting the request directly to an Infernet node over the Internet, following a pattern that goes something like:

Off-chain initiation, on-chain fulfillment

  1. Originating off-chain via an EIP-712 signed (opens in a new tab) subscription created by the developer on behalf of their contract, posted directly to an Infernet node
  2. Fulfillment on-chain via the EIP712Coordinator's deliverComputeDelegatee() function, which atomically creates a subscription and delivers a response

Introducing the Delegator

A simple way to enable this use-case is to allow developer smart contracts to sign-off on creating a new subscription, entirely off-chain. But, because smart contracts themselves don't have a private key, we can elect an Externally-owned account (EOA) (opens in a new tab) via Delegator.sol that manages this process on behalf of the contract.

Imagine we're trying to replicate the callback subscription we created in the CallbackConsumer, but with the request for compute output initiated completely off-chain:

Inherit Delegator.sol

Starting backwards from the CallbackConsumer example's final-state, we can retrofit off-chain initiation.

In your smart contract, you must inherit the Delegator.sol abstract contract found in infernet/core/pattern/Delegator.sol:

import {Delegator} from "infernet/core/pattern/Delegator.sol";
 
contract MyContract is CallbackConsumer, Delegator {
    // ...
}

Initialize the Delegator

Once inherited, you must provide the initial delegatee (EOA responsible for signing on behalf of the contract) address to the Delegator constructor:

import {Delegator} from "infernet/core/pattern/Delegator.sol";
 
contract MyContract is CallbackConsumer, Delegator {
    // ...
    constructor(
        address registry,
        address delegatee
    ) CallbackConsumer(registry) Delegator(delegatee) {}
    // ...
}

Allow updating delegatee

By default, the Delegator exposes an _updateSigner function (technical reference) that allows the inheriting smart contract to update the assigned delegatee. You may choose to optionally expose this function.

️⚠️

Note that you should only expose _updateSigner to authorized callers in your smart contract, since it allows unilaterally issuing subscriptions on its' behalf.

import {Delegator} from "infernet/core/pattern/Delegator.sol";
 
contract MyContract is CallbackConsumer, Delegator {
    // ...
    constructor(
        address registry,
        address delegatee
    ) CallbackConsumer(registry) Delegator(delegatee) {}
 
    function updateSigner(address newSigner) external onlyAuthorized {
        _updateSigner(newSigner);
    }
    // ...
}

Submit off-chain subscriptions

Now, your delegatee address can sign-off on off-chain Subscriptions, fulfilled by the EIP712Coordinator's createSubscriptionDelegatee() and deliverComputeDelegatee() functions.

Best practices

  1. The EIP712Coordinator does not enforce you to use a monotonically-increasing nonce. This is to prevent situations where successive delegated transactions depend upon one another. A maxSubscriberNonce() function is exposed in the Coordinator that keeps track of the highest nonce your contract has used. We recommend incrementing this value serially in your off-chain signing processes to prevent collisions.