Uniroll supports the integration of solver networks to allow solvers to leverage shared settlement. Solvers create and fill intents on the spoke contracts while offchain agents handle settlement.
Uniroll is still in active development. These interfaces are subject to change.
Introduction
This guide will walk you through the integration of an intent protocol into the clearing chain. The intent protocol used in this example has a network of solvers that will relay a greeting from one chain to another for a fee.
Creating an Intent
contract Greeter {// Storage IERC20 public token; IUnirollSpoke public uniroll;// Destination chain configuration for Greeteruint32public destination;addresspublic destinationToken;addresspublic destinationGreeter;// Stored greetingstringpublic greeting;// EventseventGreetingDispatched(bytes32indexed id, uin256 fee, string greeting);constructor(address_token,address_uniroll,uint32_destination,address_destinationToken,address_destinationGreeter ) { token =IERC20(_token); uniroll =IUniroll(_uniroll); destination = _destination; destinationToken = _destinationToken; destinationGreeter = _destinationGreeter; greeting ="Pay me for change"; }functionsetGreeting(stringmemory_greeting,uint256_fee) public {// Approve token to connext token.approve(address(uniroll), _fee);// Create the intent databytesmemory data =bytes(_greeting);// Call new intent (bytes32 intentId, Intent memory intent) = uniroll.newIntent( destination, destinationGreeter,address(token), destinationToken, _fee, data );emitGreetingDispatched(intentId, fee, greeting); }}
Once the GreetingDispatched event is emitted, a message to the clearing chain is queued and solvers of the greeter network should competing to fill that intent. Uniroll makes no assumptions about solver validity or intent discovery.
Filling Intents
Once the solver is ready to execute, they can call fillIntent on the destination spoke contract. Before calling fillIntent, solvers should ensure they have sufficient balance of the output asset on the spoke contracts (this can be done in the same call):
Copy
contract GreeterSolverProxy { Greeter public greeter;eventGreeterIntentFilled(addressindexed caller, Intent intent);constructor(address_greeter) { greeter =Greeter(_greeter); }functiondepositAndFill(address_fundingSource,Intentmemory_intent) public {// Pull tokens from funding sourceIERC20(_intent.outputAsset).transferFrom(_fundingSource,address(this), _intent.amount);// Approve spokeIERC20(_intent.outputAsset).approve(address(greeter), _intent.amount);// Deposit greeter.connext.deposit(_intent.outputAsset, _intent.amount);// Fill intentfill(_intent); }functionfill(Intentmemory_intent) public { greeter.connext.fill(intent);emitGreeterIntentFilled(msg.sender, _intent); }}
Once the intent is filled, a message to the clearing chain is queued. As soon as the intent and fill messages arrive to the clearing chain, the intent is ready to be settled.
Settlement
Once intent and fill messages arrive on the clearing chain, they are ready to be settled. When grouping intents into settlements, you should do the following for each intent to be settled:
Check the supported chain by the solver.
Check the available liquidity on each chain.
If there is sufficient liquidity to settle on the target chain (i.e. the chain that the solver executed on), settle there.
Otherwise, settle to the chain that has the highest liquidity that the solver also supports.
Offchain agents will automatically process messages and settlements in batches. Solvers are welcome to expedite settlement by calling the following methods on the ConnextSpoke contracts:
If you choose to self-process your settlement, you will incur the costs of the other enqueued intents.
contract GreeterSolverProxy { ISpokeGateway gateway;eventFillQueueProcessed(uint32 number, uint256 fee);eventIntentQueueProcessed(uint32 number, uint256 fee);// ...// This function will require callers to pass in the appropriate messaging fee.// This fee can be estimated using `ISpokeGateway.quoteMessage`.// The queue size can be retrieved from the subgraphs. This is called on the destination// chain of the intent.functionfillAndProcess(Intentmemory_intent,uint32_queueSize) publicpayable {fill(_intent); greeter.connext.processFillQueue{value: msg.value}(_queueSize +1);emitFillQueueProcessed(_queueSize +1, msg.value); }// Both message queues must arrive on the clearing chain for the intent to be// elligible for settlement. This function must be called on the intent origin.functionprocessIntentQueue(uint32_queueSize) publicpayable { greeter.connext.processIntentQueue{value: msg.value}(_queueSize);emitIntentQueueProcessed(_queueSize, msg.value); }}
These queue methods will dispatch messages to the clearing chain. Once both messages arrive, the intent can be settled by offchain agents.
The settlement functions are non-custodial, so it is possible to self-settle. At the moment, it is non-trivial to get the settlement payloads but SDK methods will be exposed in the future.
Monitoring Intent Status
Intents can be monitored using the subgraphs. See the playgrounds here to explore the entities!
Remember that the subgraphs are not natively crosschain, so if a destination intent exists the intent is filled. Similarly, if a hub intent exists the status can be used to determine which messages need to arrive or if the intent is ready to be settled.