Delegator
By default, Subscriptions are limited to:
On-chain initiation, on-chain fulfillment
- Originating on-chain via the
Coordinator
'screateSubscription()
function or inherited consumers - Fulfillment on-chain via the
Coordinator
'sdeliverCompute()
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
- 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
- Fulfillment on-chain via the
EIP712Coordinator
'sdeliverComputeDelegatee()
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
- The
EIP712Coordinator
does not enforce you to use a monotonically-increasingnonce
. This is to prevent situations where successive delegated transactions depend upon one another. AmaxSubscriberNonce()
function is exposed in theCoordinator
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.