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.
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.