Skip to main content

πŸƒ What is Lilypad?

Full Documentation​

Lilypad Docs

Vision​

Lilypad is aiming to build an internet-scale trustless distributed compute network for web3. Creating the infrastructure for use cases like AI inference, ML training, DeSci and more.

Overview​

Lilypad (v0) currently enables users to access verifiable, distributed off-chain compute directly from smart contracts.

Lilypad is at v0 and is a Proof of Concept project operating as an integration layer between Bacalhau compute jobs and solidity smart contracts. This integration enables users to access verifiable off-chain decentralised compute from DApps and smart contract projects, enabling interactions and innovations between on-chain and off-chain compute.

Lilypad v0 does not charge for compute jobs, outside of network running fees (ie. the cost of transactions on the blockchain network it is deployed to). It operates on the Bacalhau public compute network (which is free to use), though it is worth noting that there are no reliability guarantees given for this network (which is something future versions of this protocol will be working to improve\

Lilypad Roadmap​

  • v0: September 2022 - Lilypad Bridge POC for triggering and returning Bacalhau compute jobs from a smart contract
  • v1: July 2023 - A modicum-based minimal testnet (EVM-based). See github
  • v2: September 2023 - A more robust trustless distributed testnet
  • v3: tbd - Lilypad Mainnet

Lilypad Roadmap

Lilypad v0 Reference

Architecture​

Overview​

Lilypad is a β€˜bridge’ to enable computation jobs from smart contracts. The aim of Lilypad v0 was to create an integration for users to call Bacalhau jobs directly from their solidity smart contracts and hence enable interactions and innovations between on-chain and off-chain compute.

Lilypad v0 is a proof of concept bridge which runs off the public (free to use) Bacalhau compute network. As such, the reliability of jobs on this network are not guaranteed.

If you have a need for reliable compute based on this infrastructure - get in touch with us.

Lilypad v0 on the FVM Network

A user contract implements the LilypadCaller interface and to call a job, they make a function call to the deployed LilypadEvents contract.

This contract emits an event which the Lilypad bridge daemon listens for and then forwards on to the Bacalhau network for processing.

Once the job is complete, the results are returned back to the originating user contract from the bridge code.

Note: runBacalhauJob() is now runLilypadJob()

See more about how Bacalhau & Lilypad are related below:

Lilypad v0 Quick Start​

Prefer Video?​

info

Note: Since this video was released some changes have been made to the underlying code, but the process and general architecture remains the same.

Quick Start Guide​

info

The Lilypad Contracts are not currently importable via npm (though this is in progress), so to import them to you own project, you'll need to use their github links

Using Lilypad in your own solidity smart contract requires the following steps

  1. Create a contract that implements the LilypadCaller interface.

    As part of this interface you need to implement 2 functions:

    • lilypadFulfilled - a callback function that will be called when the job completes successfully
    • lilypadCancelled - a callback function that will be called when the job fails
  2. Provide a public Docker Spec compatible for use on Bacalhau in JSON format to the contract.

  3. To trigger a job from your contract, you need to call the LilypadEvents contract which the Lilypad bridge is listening to and which connects to the Bacalhau public network. Create an instance of LilypadEvents by passing the public contract address on the network you are using (see deployed network details) to the LilypadEvents constructor.

  4. Call the LilypadEvents contract function runLilypadJob() passing in the following parameters.

NameTypePurpose
_fromaddressThe address of the calling contract, to which success or failure will be passed back. You should probably use address(this) from your contract.
_specstringA Bacalhau job spec in JSON format. See below for more information on creating a job spec.
_resultTypeLilypadResultTypeThe type of result that you want to be returned. If you specify CID, the result tree will come back as a retrievable IPFS CID. If you specify StdOut, StdErr or ExitCode, those raw values output from the job will be returned.

Implement the LilypadCaller Interface in your contract​

Create a contract that implements LilypadCallerInterface. As part of this interface you need to implement 2 functions:

  • lilypadFulfilled - a callback function that will be called when the job completes successfully
  • lilypadCancelled - a callback function that will be called when the job fails
  /** === LilypadCaller Interface === **/
pragma solidity >=0.8.4;
import 'https://github.com/bacalhau-project/lilypad/blob/main/hardhat/contracts/LilypadCallerInterface.sol' //Location of file link

/** === User Contract Example === **/
contract MyContract is LilypadCallerInterface {

function lilypadFulfilled(address _from, uint _jobId,
LilypadResultType _resultType, string calldata _result)
external override {
// Do something when the LilypadEvents contract returns
// results successfully
}

function lilypadCancelled(address _from, uint _jobId, string
calldata _errorMsg) external override {
// Do something if there's an error returned by the
// LilypadEvents contract
}
}

Add a Spec compatible with Bacalhau​

info

There are several public examples you can try out without needing to know anything about Docker or WASM specification jobs -> see the Bacalhau Docs. The full specification for Bacalhau jobs can be seen here.

Bacalhau operates by executing jobs within containers. This means it is able to run any arbitrary Docker jobs or WASM images

We'll use the public Stable Diffusion Docker Container located here for this example.

Here's an example JSON job specification for the Stable Diffusion job:

{
"Engine": "docker",
"Verifier": "noop",
"PublisherSpec": { "Type": "ipfs" },
"Docker": {
"Image": "ghcr.io/bacalhau-project/examples/stable-diffusion-gpu:0.0.1",
"Entrypoint": ["python"],
"Parameters": [
"main.py",
"--o",
"./outputs",
"--p",
"A User Prompt Goes here"
]
},
"Resources": { "GPU": "1" },
"Outputs": [{ "Name": "outputs", "Path": "/outputs" }],
"Deal": { "Concurrency": 1 }
}

Here's an example of using this JSON specification in solidity:

Note that since we need to be able to add the user prompt input to the spec, it's been split into 2 parts.

string constant specStart = '{'
'"Engine": "docker",'
'"Verifier": "noop",'
'"PublisherSpec": {"Type": "ipfs"},'
'"Docker": {'
'"Image": "ghcr.io/bacalhau-project/examples/stable-diffusion-gpu:0.0.1",'
'"Entrypoint": ["python"],
'"Parameters": ["main.py", "--o", "./outputs", "--p", "';

string constant specEnd =
'"]},'
'"Resources": {"GPU": "1"},'
'"Outputs": [{"Name": "outputs", "Path": "/outputs"}],'
'"Deal": {"Concurrency": 1}'
'}';


//Example of use:
string memory spec = string.concat(specStart, _prompt, specEnd);

Add the Lilypad Events Address & Network Fee​

You can do this by either passing it into your constructor or setting it as a variable

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
import "https://github.com/bacalhau-project/lilypad/blob/main/hardhat/contracts/LilypadEventsUpgradeable.sol";
import "https://github.com/bacalhau-project/lilypad/blob/main/hardhat/contracts/LilypadCallerInterface.sol";

/** === User Contract Example === **/
contract MyContract is LilypadCallerInterface {
address public bridgeAddress; // LilypadEvents contract address for interacting with the deployed LilypadEvents contract
LilypadEventsUpgradeable bridge; // Instance of the LilypadEvents Contract to interact with
uint256 public lilypadFee; //=30000000000000000 on FVM;

constructor(address _bridgeContractAddress) {
bridgeAddress = _bridgeContractAddress; //the LilypadEvents contract address for your network
bridge = LilypadEventsUpgradeable(_bridgeContractAddress); //create an instance of the Events Contract to interact with
uint fee = bridge.getLilypadFee(); // you can fetch the fee amount required for the contract to run also
lilypadFee = fee;
}

function lilypadFulfilled(address _from, uint _jobId,
LilypadResultType _resultType, string calldata _result)
external override {
// Do something when the LilypadEvents contract returns
// results successfully
}

function lilypadCancelled(address _from, uint _jobId, string
calldata _errorMsg) external override {
// Do something if there's an error returned by the Lilypad Job
}
}

Call the LilypadEvents runLilypadJob function​

Using the LilypadEvents Instance, we can now send jobs to the Bacalhau Network via our contract using the runLilypadJob() function.

In this example we'll use the Stable Diffusion Spec shown above in #add-a-spec-compatible-with-bacalhau

info

Note that calling the runLilypadJob() function requires a network fee. While the Bacalhau public Network is currently free to use, gas fees are still needed to return the results of the job performed. This is the payable fee in the contract.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
import "https://github.com/bacalhau-project/lilypad/blob/main/hardhat/contracts/LilypadEventsUpgradeable.sol";
import "https://github.com/bacalhau-project/lilypad/blob/main/hardhat/contracts/LilypadCallerInterface.sol";

/** === User Contract Example === **/
contract MyContract is LilypadCallerInterface {
address public bridgeAddress; // Variable for interacting with the deployed LilypadEvents contract
LilypadEventsUpgradeable bridge;
uint256 public lilypadFee; //=30000000000000000;

constructor(address _bridgeContractAddress) {
bridgeAddress = _bridgeContractAddress;
bridge = LilypadEventsUpgradeable(_bridgeContractAddress);
uint fee = bridge.getLilypadFee(); // you can fetch the fee amount required for the contract to run also
lilypadFee = fee;
}

//** Define the Bacalhau Specification */
string constant specStart = '{'
'"Engine": "docker",'
'"Verifier": "noop",'
'"PublisherSpec": {"Type": "ipfs"},'
'"Docker": {'
'"Image": "ghcr.io/bacalhau-project/examples/stable-diffusion-gpu:0.0.1",'
'"Entrypoint": ["python"],
'"Parameters": ["main.py", "--o", "./outputs", "--p", "';

string constant specEnd =
'"]},'
'"Resources": {"GPU": "1"},'
'"Outputs": [{"Name": "outputs", "Path": "/outputs"}],'
'"Deal": {"Concurrency": 1}'
'}';


/** Call the runLilypadJob() to generate a stable diffusion image from a text prompt*/
function StableDiffusion(string calldata _prompt) external payable {
require(msg.value >= lilypadFee, "Not enough to run Lilypad job");
// TODO: spec -> do proper json encoding, look out for quotes in _prompt
string memory spec = string.concat(specStart, _prompt, specEnd);
uint id = bridge.runLilypadJob{value: lilypadFee}(address(this), spec, uint8(LilypadResultType.CID));
require(id > 0, "job didn't return a value");
prompts[id] = _prompt;
}

/** LilypadCaller Interface Implementation */
function lilypadFulfilled(address _from, uint _jobId,
LilypadResultType _resultType, string calldata _result)
external override {
// Do something when the LilypadEvents contract returns
// results successfully
}

function lilypadCancelled(address _from, uint _jobId, string
calldata _errorMsg) external override {
// Do something if there's an error returned by the
// LilypadEvents contract
}
}

description: Lilypad v0 Integrated Networks​

Deployed Network Details

info

If you have a use case for another network - please get in touch with us!

The Lilypad Events contract - used for triggering compute jobs on Bacalhau, is currently integrated to the following networks on the address specified:

Lilypad v0 Deployed Networks​

Chain NameLilypadEvents Contract AddressRPCChainIDBlockExplorerFaucet
Filecoin Calibration Net (testnet)0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820https://api.calibration.node.glif.io/rpc/v0314159 (0x4cb2f)https://calibration.filscan.io/,https://faucet.calibration.fildev.network/
Filecoin Mainnet0xc18879C0a781DdFa0258302467687413AaD5a4E6https://api.node.glif.io/rpc/v1, https://filecoin-mainnet.chainstacklabs.com/rpc/v1, https://rpc.ankr.com/filecoin314 (0x13a)https://fvm.starboard.ventures/, https://explorer.glif.io/, https://beryx.zondax.ch/, https://filfox.io/Requires Filecoin token See docs
Mantle Testnet0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820https://rpc.testnet.mantle.xyz5001 (0x1389)https://explorer.testnet.mantle.xyz/https://faucet.testnet.mantle.xyz/
Sepolia Testnet0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820https://rpc2.sepolia.org, https://eth-sepolia.g.alchemy.com/v2/demo, https://rpc.sepolia.org, see https://chainlist.org/chain/1115511111155111 (0xaa36a7)https://sepolia.etherscan.io/https://www.infura.io/faucet/sepolia, https://sepoliafaucet.com/, https://sepolia-faucet.pk910.de/
Polygon Mumbai0xdC7612fa94F098F1d7BB40E0f4F4db8fF0bC8820see https://chainlist.org/chain/8000180001 (0x13881)https://mumbai.polygonscan.com/https://faucet.polygon.technology/, https://mumbaifaucet.com/
Polygon Mainnet (coming soon)see https://chainlist.org/chain/137137 (0x89)https://polygonscan.com/Requires MATIC tokens
Optimism (coming soon)see https://chainlist.org/chain/1010(0xa)Requires OP tokens
Arbitrum One (coming soon)see https://chainlist.org/chain/4216142161 (0xa4b1)Requires ARB tokens