1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^ 0.8.0 ;
3
+
4
+ import './interfaces/ICCGateway.sol ' ;
5
+ import {ICCGatewayClient} from './interfaces/ICCGatewayClient.sol ' ;
6
+
7
+ import {FunctionsClient} from "@chainlink/contracts/src/v0.8/functions/v1_0_0/FunctionsClient.sol " ;
8
+ import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol " ;
9
+ import {FunctionsRequest} from "@chainlink/contracts/src/v0.8/functions/v1_0_0/libraries/FunctionsRequest.sol " ;
10
+
11
+ contract CCGateway is ICCGateway , FunctionsClient , AccessControl {
12
+ using FunctionsRequest for FunctionsRequest.Request;
13
+
14
+ bytes32 public constant MANAGER_ROLE = keccak256 ("MANAGER_ROLE " );
15
+ bytes32 public constant CLIENT_ROLE = keccak256 ("CLIENT_ROLE " );
16
+ bytes32 private _donId; // DON ID for the Functions DON to which the requests are sent
17
+
18
+ mapping (uint64 subscriptionId = > CCGRequest) private _requests; // Each subscription can only handle one kind of request
19
+ mapping (bytes32 requestId = > ICCGatewayClient.CCGResponse) private unprocessed_responses; // Responses that have not been processed yet
20
+
21
+ constructor (address router , bytes32 initialDonId , address initialOwner ) FunctionsClient (router) {
22
+ _donId = initialDonId;
23
+
24
+ address to = address (0 ) == initialOwner ? _msgSender () : initialOwner;
25
+ _grantRole (DEFAULT_ADMIN_ROLE, to);
26
+ _grantRole (MANAGER_ROLE, to);
27
+ }
28
+
29
+ /**
30
+ * @dev Set the DON ID
31
+ * @param newDonId New DON ID
32
+ */
33
+ function setDonId (bytes32 newDonId ) external onlyRole (getRoleAdmin (MANAGER_ROLE)) {
34
+ _donId = newDonId;
35
+ }
36
+
37
+ /**
38
+ * @dev Get the DON ID
39
+ * @return DON ID
40
+ */
41
+ function donId () external view returns (bytes32 ) {
42
+ return _donId;
43
+ }
44
+
45
+ function registerRequest (
46
+ uint64 subscriptionId ,
47
+ FunctionsRequest.Location codeLocation ,
48
+ string calldata source ,
49
+ FunctionsRequest.Location secretsLocation ,
50
+ bytes calldata encryptedSecretsReference ,
51
+ uint32 callbackGasLimit ,
52
+ string calldata name
53
+ ) external onlyRole (MANAGER_ROLE) {
54
+ if (bytes (name).length == 0 ) {revert CCGRequestNameEmpty ();}
55
+
56
+ CCGRequest storage req = _requests[subscriptionId];
57
+ req.name = name;
58
+ req.callbackGasLimit = callbackGasLimit;
59
+
60
+ // Only JavaScript is supported for now
61
+ req.config.language = FunctionsRequest.CodeLanguage.JavaScript;
62
+ req.config.codeLocation = codeLocation;
63
+ req.config.source = source;
64
+ req.config.secretsLocation = secretsLocation;
65
+ req.config.encryptedSecretsReference = encryptedSecretsReference;
66
+ }
67
+
68
+
69
+ function getRequest (uint64 subscriptionId ) external view onlyManager returns (CCGRequest memory ) {
70
+ if (bytes (_requests[subscriptionId].name).length == 0 ) {revert CCGRequestNotRegistered (subscriptionId);}
71
+ return _requests[subscriptionId];
72
+ }
73
+ // ---------------------------------------------------------------------------------------------------------------------
74
+ modifier onlySource (bytes32 requestId ) {
75
+ if (unprocessed_responses[requestId].source != _msgSender ()) {revert CCGOnlySameSourceAllowed (requestId);}
76
+ _;
77
+ }
78
+
79
+ modifier onlyManager () {
80
+ if (! hasRole (MANAGER_ROLE, tx .origin )) {revert CCGOnlyManagerAllowed ();}
81
+ _;
82
+ }
83
+ // ---------------------------------------------------------------------------------------------------------------------
84
+ /**
85
+ * @notice Triggers an on-demand Functions request using remote encrypted secrets
86
+ * @param subscriptionId Subscription ID used to pay for request (FunctionsConsumer contract address must first be added to the subscription)
87
+ * @param args String arguments passed into the source code and accessible via the global variable `args`
88
+ * @param bytesArgs Bytes arguments passed into the source code and accessible via the global variable `bytesArgs` as hex strings
89
+ * @param encryptedSecretsReference Reference pointing to encrypted secrets
90
+ */
91
+ function sendRequest (
92
+ uint64 subscriptionId ,
93
+ string [] calldata args ,
94
+ bytes [] calldata bytesArgs ,
95
+ bytes calldata encryptedSecretsReference
96
+ ) external onlyRole (CLIENT_ROLE) returns (bytes32 requestId ) {
97
+ CCGRequest storage request = _requests[subscriptionId];
98
+ if (bytes (request.name).length == 0 ) {revert CCGRequestNotRegistered (subscriptionId);}
99
+
100
+ FunctionsRequest.Request memory req = request.config;
101
+ req.initializeRequest (req.codeLocation, FunctionsRequest.CodeLanguage.JavaScript, req.source);
102
+ if (encryptedSecretsReference.length > 0 ) {req.encryptedSecretsReference = encryptedSecretsReference;}
103
+
104
+ if (args.length > 0 ) {
105
+ req.setArgs (args);
106
+ }
107
+ if (bytesArgs.length > 0 ) {
108
+ req.setBytesArgs (bytesArgs);
109
+ }
110
+
111
+ requestId = _sendRequest (req.encodeCBOR (), subscriptionId, request.callbackGasLimit, _donId);
112
+ unprocessed_responses[requestId].state = ICCGatewayClient.CCGResponseState.Sent;
113
+ unprocessed_responses[requestId].source = _msgSender ();
114
+ }
115
+
116
+ /**
117
+ * @notice Store latest result/error
118
+ * @param requestId The request ID, returned by sendRequest()
119
+ * @param response Aggregated response from the user code
120
+ * @param err Aggregated error from the user code or from the execution pipeline
121
+ * Either response or error parameter will be set, but never both
122
+ */
123
+ function fulfillRequest (bytes32 requestId , bytes memory response , bytes memory err ) internal override {
124
+ ICCGatewayClient.CCGResponse storage resp = unprocessed_responses[requestId];
125
+ if (resp.state != ICCGatewayClient.CCGResponseState.Sent) {revert CCGRequestAlreadyFulfilled (requestId);}
126
+
127
+ unprocessed_responses[requestId].state = err.length > 0 ? ICCGatewayClient.CCGResponseState.Error : ICCGatewayClient.CCGResponseState.Success;
128
+ unprocessed_responses[requestId].data = response;
129
+ unprocessed_responses[requestId].error = err;
130
+
131
+ // TODO: Call the client's callback function.
132
+ ICCGatewayClient (resp.source).callback (requestId);
133
+ }
134
+
135
+ /**
136
+ * @dev Get the response data
137
+ * @param requestId The request ID, returned by sendRequest()
138
+ * @return response CCGResponse
139
+ */
140
+ function getResponse (bytes32 requestId , bool remove ) external onlySource (requestId) returns (ICCGatewayClient.CCGResponse memory response ) {
141
+ response = unprocessed_responses[requestId];
142
+ if (remove) {delete unprocessed_responses[requestId];}
143
+ }
144
+ }
0 commit comments