Skip to content

Commit

Permalink
chore: Add README.md (#290)
Browse files Browse the repository at this point in the history
  • Loading branch information
apquinit authored Aug 13, 2024
1 parent 8642439 commit fa394f2
Showing 1 changed file with 312 additions and 0 deletions.
312 changes: 312 additions & 0 deletions hardhat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
# Lilypad Smart Contracts

Table of Contents

1. [Contracts Overview](#contracts-overview)
2. [Prerequisites](#prerequisites)
3. [Installation](#installation)
4. [Deploy Smart Contracts on Arbitrum Testnet](#deploy-smart-contracts-on-arbitrum-testnet)
5. [Update Existing Smart Contracts](#update-existing-smart-contracts)
7. [Strategy for Arbitrum Mainnet Deployment](#strategy-for-arbitrum-mainnet-deployment)
8. [Running Jobs from Smart Contracts](#running-jobs-from-smart-contracts)

# Contracts Overview
## Core Contracts:
- **LilypadPayments.sol**: Manages payment flows within the ecosystem.
- **LilypadToken.sol**: Manages the ERC20-compatible Lilypad tokens.
- **LilypadStorage.sol**: Provides decentralized storage for data across the platform.
- **LilypadMediationRandom.sol**: Implements a random selection mechanism for mediators.
- **LilypadUsers.sol**: Handles user management, including registration and access control.
- **LilypadPow.sol**: Provides Proof-of-Work (PoW) functionalities.
- **LilypadOnChainJobCreator.sol**: Allows users to create and manage on-chain jobs.
- **LilypadController.sol**: Manages core functions and interactions between different components.
- **SharedStructs.sol**: Contains shared data structures used by multiple contracts.
- **ExampleClient.sol**: A demonstration contract for client interactions with Lilypad.

# Prerequisites
Ensure you have the following installed:

- Node.js (v20 or higher) and npm
- HardHat (installed globally): npm install --global hardhat
- MetaMask (or another Ethereum wallet)
- Infura API key (for interacting with Arbitrum networks)
- Etherscan API key (for verifying contracts on Etherscan)

# Installation
Step 1: Setup Environment
```bash
cp .env.sample.env
```
Update the .env file with your own environment variables.

Step 2: Install Dependencies
```bash
npm install
```

Step 3: Compile Smart Contracts
```bash
npx hardhat compile
```

Step 4: Run Tests
```bash
npx hardhat test
```

# Deploy Smart Contracts on Arbitrum Testnet
Step 1: Configure hardhat.config.js to include Arbitrum Testnet details:
```js
networks: {
testnet:{
url: 'https://sepolia-rollup.arbitrum.io/rpc',
chainId: 421614,
accounts: [getAccount('admin').privateKey],
},
},
```

Step 2: Deploy contracts to Arbitrum Testnet:
```bash
npx hardhat run deploy/{{deploy_contract}}.js --network testnet
```
There are multiple deploy scripts available for different contracts. Ensure to use the appropriate deploy script for your deployment.

Step 3: After deployment, verify contracts on the Arbiscan Testnet using:
```bash
npx hardhat verify --network testnet DEPLOYED_CONTRACT_ADDRESS
```

# Update Existing Smart Contracts
Step 1: Make Contract Changes
Edit the Solidity files located in the contracts/ directory.

Step 2: Recompile Contracts
```bash
npx hardhat compile
```

Step 3: Deploy Updated Contracts
For incremental changes:
```bash
npx hardhat run deploy/{{deploy_contract}}.js --network testnet
```
There are multiple deploy scripts available for different contracts. Ensure to use the appropriate deploy script for your deployment.

Step 4: Migrate Data (If Needed)
If your contract update affects storage, ensure to include migration scripts or processes to move data from the old contract to the new one.

# Strategy for Arbitrum Mainnet Deployment

Step 1: Finalize Contract Code
Ensure all contracts are thoroughly tested and audited. This is crucial for deploying on the mainnet.

Step 2: Create a Deployment Plan
Map out the deployment process, considering dependencies between contracts and the order of deployment.

Step 3: Update HardHat Configuration
Add Arbitrum Mainnet to your hardhat.config.js:
```js
networks: {
mainnet:{
url: 'https://arb1.arbitrum.io/rpc',
chainId: 42161,
accounts: [getAccount('admin').privateKey],
},
},
```

Step 4: Deploy to Arbitrum Mainnet:
```bash
npx hardhat run deploy/{{deploy_contract}}.js --network mainnet
```
There are multiple deploy scripts available for different contracts. Ensure to use the appropriate deploy script for your deployment.

Step 5: Monitor and Verify
After deployment, use tools like Etherscan or Arbiscan to monitor the contracts and ensure they are functioning as expected. Verify the contracts if necessary.

Step 6: Post-Deployment Actions
Set up any required governance or admin controls. Notify users or clients about the new deployment and any actions they need to take.

# Running Jobs from Smart Contracts

We have deloyed the [LilypadOnChainJobCreator](contracts/LilypadOnChainJobCreator.sol) contract which you can use to trigger running jobs on the Lilypad network from other smart contracts.

It works in tandem with the `lilypad jobcreator` on-chain which will watch the on-chain contract and manage jobs on behalf of contracts that submit them.

### Creating a Job

You will need to know the contract address for the on-chain job creator so we can submit transactions to it.

The production controller address is `0xF2fD1B9b262982F12446149A27d8901Ac68dcB59` and you can ask it for the address of the on-chain job creator `getJobCreatorAddress()`

Running a job involves 2 phases:

* calling `approve` on the ERC-20 contract to allow the solver to spend your tokens
* trigger the job via the on chain job manager

Now we know the address of the on-chain job controller - we can ask it for 3 things:

* the address of the ERC-20 token contract - `getTokenAddress()`
* how much the required deposit it - `getRequiredDeposit()`
* the address of the solver that will handle running the job for us - `getControllerAddress()`

Knowing these 3 things means we can call the standard ERC-20 `approve` to allow the solver to spend our tokens on our behalf.

Now - we can call the `runJob` method of the on chain controller from another contract. This will cause the job-creator service to kick in and do the following things:

* check that funds have been approved for the solver
* transfer those funds to it's wallet
* run the job on lilypad
* call the `submitResults` method on the on-chain job creator
* the on-chain job creator will call the `submitResults` of the original calling contract

The following is an example on-chain smart contract:

```solidity
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.6;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./ILilypadJobManager.sol";
import "./ILilypadJobClient.sol";
contract ExampleClient is Ownable, Initializable, ILilypadJobClient {
address private jobManagerAddress;
ILilypadJobManager private jobManagerContract;
mapping(uint256 => string) private jobResults;
event JobCreated(
uint256 id,
string message
);
event JobCompleted(
uint256 id,
string dealId,
string dataId
);
function initialize(address _jobManagerAddress) public initializer {
setJobManagerAddress(_jobManagerAddress);
}
function setJobManagerAddress(address _jobManagerAddress) public onlyOwner {
require(_jobManagerAddress != address(0), "Job manager address");
jobManagerAddress = _jobManagerAddress;
jobManagerContract = ILilypadJobManager(jobManagerAddress);
}
function getJobResult(uint256 _jobID) public view returns (string memory) {
return jobResults[_jobID];
}
function runCowsay(
string memory message
) public {
string[] memory inputs = new string[](1);
inputs[0] = string(abi.encodePacked("Message=", message));
uint256 id = jobManagerContract.runJob(
"cowsay:v0.0.4",
inputs,
msg.sender
);
emit JobCreated(
id,
message
);
}
function submitResults(
uint256 id,
string memory dealId,
string memory dataId
) public override {
jobResults[id] = dataId;
emit JobCompleted(
id,
dealId,
dataId
);
}
}
```

Here is an example of a script that brings all of this together:

```typescript
import bluebird from 'bluebird'
import {
connectToken,
connectJobManager,
connectExampleClient,
getWallet,
getAddress,
} from '../utils/web3'
import { ethers } from 'hardhat'

async function main() {
// it's annoying to not be able to use argv but hardhat complains about it
const message = process.env.MESSAGE || 'Hello World!'

const token = await connectToken()
const manager = await connectJobManager()
const client = await connectExampleClient()

const setRequiredDepositTx = await manager
.connect(getWallet('solver'))
.setRequiredDeposit(ethers.parseEther("2"))
await setRequiredDepositTx.wait()

const requiredDeposit = await manager.getRequiredDeposit()

console.log(`requiredDeposit: ${Number(requiredDeposit)}`)

const paytokensTx = await token
.connect(getWallet('job_creator'))
.approve(getAddress('solver'), requiredDeposit)
await paytokensTx.wait()

console.log(`tokens approved: ${paytokensTx.hash}`)

const runjobTx = await client
.connect(getWallet('job_creator'))
.runCowsay(message)
const receipt = await runjobTx.wait()
if(!receipt) throw new Error(`no receipt`)

console.log(`submitted job: ${runjobTx.hash}`)

let jobID = 0

receipt.logs.forEach(log => {
const logs = client.interface.parseLog(log as any)
if(!logs) return
jobID = Number(logs.args[0])
})

console.log(`Job ID: ${jobID}`)
console.log(`Waiting for job to be completed...`)

let result = ''

while(!result) {
result = await client.getJobResult(jobID)
if(!result) {
await bluebird.delay(1000)
}
}

console.log(`Job result: ${result}`)
}

main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

```

0 comments on commit fa394f2

Please sign in to comment.