-
Notifications
You must be signed in to change notification settings - Fork 1
/
FunctionsConsumerNFT.sol
186 lines (164 loc) · 6.04 KB
/
FunctionsConsumerNFT.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {FunctionsClient} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/FunctionsClient.sol";
import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
import {FunctionsRequest} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/libraries/FunctionsRequest.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract FunctionsConsumerExample is FunctionsClient, ConfirmedOwner, ERC721 {
using FunctionsRequest for FunctionsRequest.Request;
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
string private _baseTokenURI;
bytes32 public s_lastRequestId;
bytes32 public s_lastResponse;
bytes32 public s_lastError;
uint32 public s_lastResponseLength;
uint32 public s_lastErrorLength;
error UnexpectedRequestID(bytes32 requestId);
event Response(bytes32 indexed requestId, bytes32 response, bytes32 err);
constructor(
address router, string memory baseTokenURI
) FunctionsClient(router) ConfirmedOwner(msg.sender) ERC721("MyNFT", "NFT") {
_baseTokenURI = baseTokenURI;
}
// ERC721
function _baseURI() internal view override returns (string memory) {
return _baseTokenURI;
}
function setBaseURI(string memory baseTokenURI) public onlyOwner {
_baseTokenURI = baseTokenURI;
}
function safeMint(address to) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
}
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
require(_exists(tokenId), "Token does not exist");
string memory baseURI = _baseURI();
string memory image = string(abi.encodePacked(
baseURI,
Strings.toString(tokenId),
"_img.png"
));
string memory metadata = string(abi.encodePacked(
baseURI,
Strings.toString(tokenId),
".json"
));
string memory json = string(abi.encodePacked(
'{"description": "My Functions NFT", ',
'"image": "', image, '", ',
'"metadata": "', metadata, '"}'
));
return json;
}
/** Chainlink Functions
* @notice Send a simple request
* @param source JavaScript source code
* @param encryptedSecretsUrls Encrypted URLs where to fetch user secrets
* @param donHostedSecretsSlotID Don hosted secrets slotId
* @param donHostedSecretsVersion Don hosted secrets version
* @param args List of arguments accessible from within the source code
* @param bytesArgs Array of bytes arguments, represented as hex strings
* @param subscriptionId Billing ID
*/
function sendRequest(
string memory source,
bytes memory encryptedSecretsUrls,
uint8 donHostedSecretsSlotID,
uint64 donHostedSecretsVersion,
string[] memory args,
bytes[] memory bytesArgs,
uint64 subscriptionId,
uint32 gasLimit,
bytes32 jobId
) external onlyOwner returns (bytes32 requestId) {
FunctionsRequest.Request memory req;
req.initializeRequestForInlineJavaScript(source);
if (encryptedSecretsUrls.length > 0)
req.addSecretsReference(encryptedSecretsUrls);
else if (donHostedSecretsVersion > 0) {
req.addDONHostedSecrets(
donHostedSecretsSlotID,
donHostedSecretsVersion
);
}
if (args.length > 0) req.setArgs(args);
if (bytesArgs.length > 0) req.setBytesArgs(bytesArgs);
s_lastRequestId = _sendRequest(
req.encodeCBOR(),
subscriptionId,
gasLimit,
jobId
);
return s_lastRequestId;
}
/**
* @notice Send a pre-encoded CBOR request
* @param request CBOR-encoded request data
* @param subscriptionId Billing ID
* @param gasLimit The maximum amount of gas the request can consume
* @param jobId ID of the job to be invoked
* @return requestId The ID of the sent request
*/
function sendRequestCBOR(
bytes memory request,
uint64 subscriptionId,
uint32 gasLimit,
bytes32 jobId
) external onlyOwner returns (bytes32 requestId) {
s_lastRequestId = _sendRequest(
request,
subscriptionId,
gasLimit,
jobId
);
return s_lastRequestId;
}
/**
* @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
* 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);
emit Response(requestId, s_lastResponse, s_lastError);
}
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;
}
}