-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(chore): Move Functions v1.0.0 to production (#10941)
* Move Functions v0 code out of dev folder * Rename Functions dev folder 1_X to reflect next version * Add PR number * Soothe linter errors for Functions 1.X
- Loading branch information
1 parent
47902e1
commit a466aea
Showing
67 changed files
with
3,426 additions
and
178 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {IFunctionsRouter} from "./interfaces/IFunctionsRouter.sol"; | ||
import {IFunctionsClient} from "./interfaces/IFunctionsClient.sol"; | ||
|
||
import {FunctionsRequest} from "./libraries/FunctionsRequest.sol"; | ||
|
||
/// @title The Chainlink Functions client contract | ||
/// @notice Contract developers can inherit this contract in order to make Chainlink Functions requests | ||
abstract contract FunctionsClient is IFunctionsClient { | ||
using FunctionsRequest for FunctionsRequest.Request; | ||
|
||
IFunctionsRouter internal immutable i_router; | ||
|
||
event RequestSent(bytes32 indexed id); | ||
event RequestFulfilled(bytes32 indexed id); | ||
|
||
error OnlyRouterCanFulfill(); | ||
|
||
constructor(address router) { | ||
i_router = IFunctionsRouter(router); | ||
} | ||
|
||
/// @notice Sends a Chainlink Functions request | ||
/// @param data The CBOR encoded bytes data for a Functions request | ||
/// @param subscriptionId The subscription ID that will be charged to service the request | ||
/// @param callbackGasLimit the amount of gas that will be available for the fulfillment callback | ||
/// @return requestId The generated request ID for this request | ||
function _sendRequest( | ||
bytes memory data, | ||
uint64 subscriptionId, | ||
uint32 callbackGasLimit, | ||
bytes32 donId | ||
) internal returns (bytes32) { | ||
bytes32 requestId = i_router.sendRequest( | ||
subscriptionId, | ||
data, | ||
FunctionsRequest.REQUEST_DATA_VERSION, | ||
callbackGasLimit, | ||
donId | ||
); | ||
emit RequestSent(requestId); | ||
return requestId; | ||
} | ||
|
||
/// @notice User defined function to handle a response from the DON | ||
/// @param requestId The request ID, returned by sendRequest() | ||
/// @param response Aggregated response from the execution of the user's source code | ||
/// @param err Aggregated error from the execution of the user code or from the execution pipeline | ||
/// @dev Either response or error parameter will be set, but never both | ||
function _fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal virtual; | ||
|
||
/// @inheritdoc IFunctionsClient | ||
function handleOracleFulfillment(bytes32 requestId, bytes memory response, bytes memory err) external override { | ||
if (msg.sender != address(i_router)) { | ||
revert OnlyRouterCanFulfill(); | ||
} | ||
_fulfillRequest(requestId, response, err); | ||
emit RequestFulfilled(requestId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
69 changes: 69 additions & 0 deletions
69
contracts/src/v0.8/functions/dev/v1_X/example/FunctionsClientExample.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {FunctionsClient} from "../FunctionsClient.sol"; | ||
import {ConfirmedOwner} from "../../../../shared/access/ConfirmedOwner.sol"; | ||
import {FunctionsRequest} from "../libraries/FunctionsRequest.sol"; | ||
|
||
/// @title Chainlink Functions example Client contract implementation | ||
contract FunctionsClientExample is FunctionsClient, ConfirmedOwner { | ||
using FunctionsRequest for FunctionsRequest.Request; | ||
|
||
uint32 public constant MAX_CALLBACK_GAS = 70_000; | ||
|
||
bytes32 public s_lastRequestId; | ||
bytes32 public s_lastResponse; | ||
bytes32 public s_lastError; | ||
uint32 public s_lastResponseLength; | ||
uint32 public s_lastErrorLength; | ||
|
||
error UnexpectedRequestID(bytes32 requestId); | ||
|
||
constructor(address router) FunctionsClient(router) ConfirmedOwner(msg.sender) {} | ||
|
||
/// @notice Send a simple request | ||
/// @param source JavaScript source code | ||
/// @param encryptedSecretsReferences Encrypted secrets payload | ||
/// @param args List of arguments accessible from within the source code | ||
/// @param subscriptionId Billing ID | ||
function sendRequest( | ||
string calldata source, | ||
bytes calldata encryptedSecretsReferences, | ||
string[] calldata args, | ||
uint64 subscriptionId, | ||
bytes32 jobId | ||
) external onlyOwner { | ||
FunctionsRequest.Request memory req; | ||
req._initializeRequestForInlineJavaScript(source); | ||
if (encryptedSecretsReferences.length > 0) req._addSecretsReference(encryptedSecretsReferences); | ||
if (args.length > 0) req._setArgs(args); | ||
s_lastRequestId = _sendRequest(req._encodeCBOR(), subscriptionId, MAX_CALLBACK_GAS, jobId); | ||
} | ||
|
||
/// @notice Store latest result/error | ||
/// @param requestId The request ID, returned by sendRequest() | ||
/// @param response Aggregated response from the user code | ||
/// @param err Aggregated error from the user code or from the execution pipeline | ||
/// @dev Either response or error parameter will be set, but never both | ||
function _fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override { | ||
if (s_lastRequestId != requestId) { | ||
revert UnexpectedRequestID(requestId); | ||
} | ||
// Save only the first 32 bytes of response/error to always fit within MAX_CALLBACK_GAS | ||
s_lastResponse = _bytesToBytes32(response); | ||
s_lastResponseLength = uint32(response.length); | ||
s_lastError = _bytesToBytes32(err); | ||
s_lastErrorLength = uint32(err.length); | ||
} | ||
|
||
function _bytesToBytes32(bytes memory b) private pure returns (bytes32 out) { | ||
uint256 maxLen = 32; | ||
if (b.length < 32) { | ||
maxLen = b.length; | ||
} | ||
for (uint256 i = 0; i < maxLen; ++i) { | ||
out |= bytes32(b[i]) >> (i * 8); | ||
} | ||
return out; | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
155 changes: 155 additions & 0 deletions
155
contracts/src/v0.8/functions/dev/v1_X/libraries/FunctionsRequest.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.19; | ||
|
||
import {CBOR} from "../../../../vendor/solidity-cborutils/v2.0.0/CBOR.sol"; | ||
|
||
/// @title Library for encoding the input data of a Functions request into CBOR | ||
library FunctionsRequest { | ||
using CBOR for CBOR.CBORBuffer; | ||
|
||
uint16 public constant REQUEST_DATA_VERSION = 1; | ||
uint256 internal constant DEFAULT_BUFFER_SIZE = 256; | ||
|
||
enum Location { | ||
Inline, // Provided within the Request | ||
Remote, // Hosted through remote location that can be accessed through a provided URL | ||
DONHosted // Hosted on the DON's storage | ||
} | ||
|
||
enum CodeLanguage { | ||
JavaScript | ||
// In future version we may add other languages | ||
} | ||
|
||
struct Request { | ||
Location codeLocation; // ════════════╸ The location of the source code that will be executed on each node in the DON | ||
Location secretsLocation; // ═════════╸ The location of secrets that will be passed into the source code. *Only Remote secrets are supported | ||
CodeLanguage language; // ════════════╸ The coding language that the source code is written in | ||
string source; // ════════════════════╸ Raw source code for Request.codeLocation of Location.Inline, URL for Request.codeLocation of Location.Remote, or slot decimal number for Request.codeLocation of Location.DONHosted | ||
bytes encryptedSecretsReference; // ══╸ Encrypted URLs for Request.secretsLocation of Location.Remote (use addSecretsReference()), or CBOR encoded slotid+version for Request.secretsLocation of Location.DONHosted (use addDONHostedSecrets()) | ||
string[] args; // ════════════════════╸ String arguments that will be passed into the source code | ||
bytes[] bytesArgs; // ════════════════╸ Bytes arguments that will be passed into the source code | ||
} | ||
|
||
error EmptySource(); | ||
error EmptySecrets(); | ||
error EmptyArgs(); | ||
error NoInlineSecrets(); | ||
|
||
/// @notice Encodes a Request to CBOR encoded bytes | ||
/// @param self The request to encode | ||
/// @return CBOR encoded bytes | ||
function _encodeCBOR(Request memory self) internal pure returns (bytes memory) { | ||
CBOR.CBORBuffer memory buffer = CBOR.create(DEFAULT_BUFFER_SIZE); | ||
|
||
buffer.writeString("codeLocation"); | ||
buffer.writeUInt256(uint256(self.codeLocation)); | ||
|
||
buffer.writeString("language"); | ||
buffer.writeUInt256(uint256(self.language)); | ||
|
||
buffer.writeString("source"); | ||
buffer.writeString(self.source); | ||
|
||
if (self.args.length > 0) { | ||
buffer.writeString("args"); | ||
buffer.startArray(); | ||
for (uint256 i = 0; i < self.args.length; ++i) { | ||
buffer.writeString(self.args[i]); | ||
} | ||
buffer.endSequence(); | ||
} | ||
|
||
if (self.encryptedSecretsReference.length > 0) { | ||
if (self.secretsLocation == Location.Inline) { | ||
revert NoInlineSecrets(); | ||
} | ||
buffer.writeString("secretsLocation"); | ||
buffer.writeUInt256(uint256(self.secretsLocation)); | ||
buffer.writeString("secrets"); | ||
buffer.writeBytes(self.encryptedSecretsReference); | ||
} | ||
|
||
if (self.bytesArgs.length > 0) { | ||
buffer.writeString("bytesArgs"); | ||
buffer.startArray(); | ||
for (uint256 i = 0; i < self.bytesArgs.length; ++i) { | ||
buffer.writeBytes(self.bytesArgs[i]); | ||
} | ||
buffer.endSequence(); | ||
} | ||
|
||
return buffer.buf.buf; | ||
} | ||
|
||
/// @notice Initializes a Chainlink Functions Request | ||
/// @dev Sets the codeLocation and code on the request | ||
/// @param self The uninitialized request | ||
/// @param codeLocation The user provided source code location | ||
/// @param language The programming language of the user code | ||
/// @param source The user provided source code or a url | ||
function _initializeRequest( | ||
Request memory self, | ||
Location codeLocation, | ||
CodeLanguage language, | ||
string memory source | ||
) internal pure { | ||
if (bytes(source).length == 0) revert EmptySource(); | ||
|
||
self.codeLocation = codeLocation; | ||
self.language = language; | ||
self.source = source; | ||
} | ||
|
||
/// @notice Initializes a Chainlink Functions Request | ||
/// @dev Simplified version of initializeRequest for PoC | ||
/// @param self The uninitialized request | ||
/// @param javaScriptSource The user provided JS code (must not be empty) | ||
function _initializeRequestForInlineJavaScript(Request memory self, string memory javaScriptSource) internal pure { | ||
_initializeRequest(self, Location.Inline, CodeLanguage.JavaScript, javaScriptSource); | ||
} | ||
|
||
/// @notice Adds Remote user encrypted secrets to a Request | ||
/// @param self The initialized request | ||
/// @param encryptedSecretsReference Encrypted comma-separated string of URLs pointing to off-chain secrets | ||
function _addSecretsReference(Request memory self, bytes memory encryptedSecretsReference) internal pure { | ||
if (encryptedSecretsReference.length == 0) revert EmptySecrets(); | ||
|
||
self.secretsLocation = Location.Remote; | ||
self.encryptedSecretsReference = encryptedSecretsReference; | ||
} | ||
|
||
/// @notice Adds DON-hosted secrets reference to a Request | ||
/// @param self The initialized request | ||
/// @param slotID Slot ID of the user's secrets hosted on DON | ||
/// @param version User data version (for the slotID) | ||
function _addDONHostedSecrets(Request memory self, uint8 slotID, uint64 version) internal pure { | ||
CBOR.CBORBuffer memory buffer = CBOR.create(DEFAULT_BUFFER_SIZE); | ||
|
||
buffer.writeString("slotID"); | ||
buffer.writeUInt64(slotID); | ||
buffer.writeString("version"); | ||
buffer.writeUInt64(version); | ||
|
||
self.secretsLocation = Location.DONHosted; | ||
self.encryptedSecretsReference = buffer.buf.buf; | ||
} | ||
|
||
/// @notice Sets args for the user run function | ||
/// @param self The initialized request | ||
/// @param args The array of string args (must not be empty) | ||
function _setArgs(Request memory self, string[] memory args) internal pure { | ||
if (args.length == 0) revert EmptyArgs(); | ||
|
||
self.args = args; | ||
} | ||
|
||
/// @notice Sets bytes args for the user run function | ||
/// @param self The initialized request | ||
/// @param args The array of bytes args (must not be empty) | ||
function _setBytesArgs(Request memory self, bytes[] memory args) internal pure { | ||
if (args.length == 0) revert EmptyArgs(); | ||
|
||
self.bytesArgs = args; | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
Oops, something went wrong.