Skip to main content

Smart Contract Request Execution

Oyster serverless functions can also be executed on blockchain using smart contract methods. More details about the protocol are described here. Following are the instructions to start with an example.

Prepare User Contract

A smart contract has to be developed for real-world use cases, which is going to interact with Oyster Serverless contracts to execute serverless functions.

To setup solidity development environment for Serverless Smart Contract, follow this guide.

Here is the sample User contract:

// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract User is Ownable {
using SafeERC20 for IERC20;
address public relayAddress;

IERC20 public usdcToken;

constructor(address _relayAddress, address _token, address _owner) Ownable(_owner) {
relayAddress = _relayAddress;
usdcToken = IERC20(_token);
}

event OwnerEthWithdrawal();
event OwnerUsdcWithdrawal();
error EthWithdrawalFailed();

function relayJob(
uint8 _env,
bytes32 _codehash,
bytes memory _codeInputs,
uint256 _userTimeout,
uint256 _maxGasPrice,
uint256 _usdcDeposit,
uint256 _callbackDeposit,
address _refundAccount,
address _callbackContract,
uint256 _callbackGasLimit
) external returns (bool) {
usdcToken.safeIncreaseAllowance(relayAddress, _usdcDeposit);

(bool success, ) = relayAddress.call{value: _callbackDeposit}(
abi.encodeWithSignature(
"relayJob(uint8,bytes32,bytes,uint256,uint256,address,address,uint256)",
_env,
_codehash,
_codeInputs,
_userTimeout,
_maxGasPrice,
_refundAccount,
_callbackContract,
_callbackGasLimit
)
);
return success;
}

event CalledBack(
uint256 indexed jobId,
address jobOwner,
bytes32 codehash,
bytes codeInputs,
bytes outputs,
uint8 errorCode
);

function oysterResultCall(
uint256 _jobId,
address _jobOwner,
bytes32 _codehash,
bytes calldata _codeInputs,
bytes calldata _output,
uint8 _errorCode
) public {
emit CalledBack(_jobId, _jobOwner, _codehash, _codeInputs, _output, _errorCode);
}

function withdrawEth() external onlyOwner {
(bool success, ) = msg.sender.call{value: address(this).balance}("");
if (!success) revert EthWithdrawalFailed();

emit OwnerEthWithdrawal();
}

function withdrawUsdc() external onlyOwner {
uint256 balance = usdcToken.balanceOf(address(this));
usdcToken.transfer(owner(), balance);

emit OwnerUsdcWithdrawal();
}

receive() external payable {}
}

This sample contract calls relayJob method on the Serverless contract to create a job.

Next, deploy sample contract which require three constructor arguments, Serverless contract address, USDC token address and owner, as instructed here.

Transfer ETH to deployed user contract address to be used as deposit for response callbacks. Follow this Metamask Guide to transfer ETH. User contract address is used as recipient and amount of 0.01 ETH is sufficient for this example.

Transfer USDC tokens to deployed user contract address to be used as deposit and payment for serverless job. USDC token contract used here is deployed at address 0xaf88d065e77c8cC2239327C5EDb3A432268e5831. Send 5000 USDC for this example.

Create a serverless job

Send a transaction to call relayJob method of the sample contract. Take a look at the sample transaction 0xa1fd499c5592cd0101f6e7a7f7e2c7379098e672556af342f914c2bba16fcbf9.

Create Transaction codeHash

This job is using the serverless function stored at 0x9468bb6a8e85ed11e292c8cac0c1539df691c8d8ec62e7dbfa9f1bd7f504e46e which returns the factors of given integer.

codeInputs

The input argument is UTF-8 encoded string with value {"num":600}. This tool can be used to convert string to hexadecimal strings for input.

userTimeout

Serverless function should not take more than 2000 milliseconds to execute.

maxGasPrice

This price can be set on the basis of current gas prices. Usually, twice or thrice of current price is sufficient for successful callback. Also, make sure it's greater than current gas price.

usdcDeposit

Subscription Relay Contract deposits 5000 USDC from user contract.

callbackDeposit

5000000000000000 wei, i.e. 0.005 eth, to be deposited for response callback

refundAccount

It's an account address which is supposed to receive any compensation, if, for any reason, job is failed to complete.

callbackContract

It has been set to the address of user contract, since it defines the callback method.

callbackGasLimit

Callback method defined in the contract above won't consume more than 5000 gas, hence used as limit.

This method call will place a job on a relay contract. Make sure that user contract sends enough value (in gas currency) while calling the relay contract to execute the callback, otherwise job won’t be placed.

Request Complete Callback

Once request has been completed, relay contract will call ‘oysterResultCall’ method of user contract. To validate the callback, check the event ‘CalledBack’ emitted by sample contract with output in event data. For example, check events here.