Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

claim token #48

Merged
merged 1 commit into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 4 additions & 28 deletions packages/hardhat/contracts/upDevAccountOwnership.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,37 +32,17 @@ contract upDevAccountOwnership is LSP8Mintable {
_LSP8_TOKENID_TYPE_NUMBER // type of NFT/ tokenIds
)
{
// set token type
_setData(_LSP4_TOKEN_TYPE_DATA_KEY, abi.encode(TokenType.COLLECTION));
}

error Soulbound();

struct TokenData { // TODO tmp
string source;
string id;
}

mapping (bytes32 => TokenData) public tokenData;

function getTokenDataForAddress(address _address) external view returns (TokenData[] memory) { // TODO
bytes32[] memory tokenIds = tokenIdsOf(_address);
TokenData[] memory result = new TokenData[](tokenIds.length);

for (uint256 i = 0; i < tokenIds.length; i++) {
result[i] = tokenData[tokenIds[i]];
}

return result;
}

function mintTmp( // TODO tmp solution for the ChainLink/Lukso bug
function mint(
address to,
bytes32 tokenId,
bool force,
string memory source,
string memory id
) public onlyOwner {
bytes memory data
) public override onlyOwner {
if (_tokenOwners[tokenId] != address(0)) {
if (_tokenOwners[tokenId] == to) {
return;
Expand All @@ -71,11 +51,7 @@ contract upDevAccountOwnership is LSP8Mintable {
return;
}
_mint(to, tokenId, force, "0x");
tokenData[tokenId] = TokenData({
source: source,
id: id
});
// _setData(tokenId, data); TODO
_setData(tokenId, data);
}

function transfer(
Expand Down
161 changes: 73 additions & 88 deletions packages/hardhat/contracts/upDevFunctionsConsumer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,30 @@ contract upDevFunctionsConsumer is FunctionsClient, ConfirmedOwner {
address up;
string source;
string id;
bool isOwned;
bytes32 tokenId;
bytes tokenData;
bool isFinished;
bool isOwned;
bool isClaimed;
}

upDevAccountOwnership public collection;

mapping(string => Source) public source;
mapping(bytes32 requiestId => Request) public requests;

string[] public availableSources;

// Custom error type
error SourceNameBusy();

// Event to log responses
event Response(
address indexed up,
bytes32 indexed requestId,
address indexed up,
bool indexed isOwned,
string source,
string id,
bool isOwned,
bytes response,
bytes err
string id
);

// State variables to store the last request ID, response, and error TODO remove?
bytes32 public s_lastRequestId;
bytes public s_lastResponse;
bytes public s_lastError;
mapping(string => Source) public source;
mapping(bytes32 requiestId => Request) public request;
mapping(address up => bytes32[] requests) public upRequests;
mapping(bytes32 tokenId => bytes32 requestId) public token;

// TODO array or mapping to get not claimed tokens

string[] public availableSources;
upDevAccountOwnership public collection;

// Router address - Hardcoded for Mumbai
// Check to get the router address for your supported network https://docs.chain.link/chainlink-functions/supported-networks
Expand All @@ -72,6 +67,10 @@ contract upDevFunctionsConsumer is FunctionsClient, ConfirmedOwner {
bytes32 donID =
0x66756e2d706f6c79676f6e2d6d756d6261692d31000000000000000000000000;

// Custom error type
error SourceNameBusy();
error AlreadyClaimed();

/**
* @notice Initializes the contract with the Chainlink router address and sets the contract owner
*/
Expand All @@ -95,7 +94,7 @@ contract upDevFunctionsConsumer is FunctionsClient, ConfirmedOwner {
"const { data } = apiResponse;"
"const urlFound = data.some(row => row.url.toLowerCase().includes(upAddress));"
"return Functions.encodeUint256(urlFound ? 1 : 0);",
false
false
);
addSource(
"buidlguidl",
Expand Down Expand Up @@ -136,12 +135,21 @@ contract upDevFunctionsConsumer is FunctionsClient, ConfirmedOwner {
availableSources.push(name);
}

function getAvailableSources() public view returns (Source[] memory) {
Source[] memory sources = new Source[](availableSources.length);
for (uint256 i = 0; i < availableSources.length; i++) {
sources[i] = source[availableSources[i]];
function getAvailableSources() external view returns (Source[] memory) {
Source[] memory sources = new Source[](availableSources.length);
for (uint256 i = 0; i < availableSources.length; i++) {
sources[i] = source[availableSources[i]];
}
return sources;
}

function getUPRequests(address up) external view returns (Request[] memory) {
uint256 numRequests = upRequests[up].length;
Request[] memory result = new Request[](numRequests);
for (uint256 i = 0; i < numRequests; i++) {
result[i] = request[upRequests[up][i]];
}
return sources;
return result;
}

/**
Expand Down Expand Up @@ -172,21 +180,23 @@ contract upDevFunctionsConsumer is FunctionsClient, ConfirmedOwner {
args[1] = id;
req.setArgs(args);

// Send the request and store the request ID
s_lastRequestId = _sendRequest(
requestId = _sendRequest(
req.encodeCBOR(),
subscriptionId,
gasLimit,
donID
);
requests[s_lastRequestId] = Request({
request[requestId] = Request({
up: up,
id: id,
source: sourceName,
id: id,
tokenId: keccak256(abi.encodePacked(sourceName, id)), // TODO encode on claim?
tokenData: abi.encode(sourceName, id),
isFinished: false,
isOwned: false,
isFinished: false
isClaimed: false
});
return s_lastRequestId;
upRequests[up].push(requestId);
}

/**
Expand All @@ -200,60 +210,48 @@ contract upDevFunctionsConsumer is FunctionsClient, ConfirmedOwner {
bytes memory response,
bytes memory err
) internal override {
// Update the contract's state variables with the response and any errors
s_lastResponse = response;
s_lastError = err;

bool isOwned = isTrue(response);

requests[requestId].isOwned = isOwned;
requests[requestId].isFinished = true;

// Emit an event to log the response
request[requestId].isFinished = true;
if (isTrue(response)) {
request[requestId].isOwned = true;
token[request[requestId].tokenId] = requestId;
}
emit Response(
requests[requestId].up,
requestId,
requests[requestId].source,
requests[requestId].id,
isOwned,
s_lastResponse,
s_lastError
request[requestId].up,
request[requestId].isOwned,
request[requestId].source,
request[requestId].id
);
}

// if (!isOwned) {
// return;
// }

// collection.mintTmp( // TODO doesn't work for all UP addresses for some reason
// requests[requestId].up,
// keccak256(abi.encodePacked(requests[requestId].source, requests[requestId].id)),
// true,
// requests[requestId].source, // TODO Chainlink fails on abi.encode for some reason, so just pass strings for now
// requests[requestId].id
// );
function claimToken(bytes32 tokenId) external {
bytes32 id = token[tokenId];
if (request[id].isClaimed) {
revert AlreadyClaimed();
}
collection.mint(
request[id].up,
request[id].tokenId,
false,
request[id].tokenData
);
request[id].isClaimed = true;
}

/**
* HELPERS
*/
function stringToAddress(
string memory _addressString
) public pure returns (address) {
// Parse the string as bytes
bytes memory _addressBytes = bytes(_addressString);

// Check that the length is 40 (hexadecimal representation of an Ethereum address)
require(_addressBytes.length == 40, "Invalid address string length");
bytes32 targetPattern =
0x0000000000000000000000000000000000000000000000000000000000000001;
bytes targetBytes = abi.encodePacked(targetPattern);

// Parse the bytes into a 20-byte address
address _parsedAddress = address(
uint160(uint256(keccak256(_addressBytes)))
);

return _parsedAddress;
function isTrue(bytes memory input) internal view returns (bool) {
return
input.length == targetBytes.length &&
keccak256(input) == keccak256(targetBytes);
}

function toAsciiString(address x) public pure returns (string memory) {
function toAsciiString(address x) internal pure returns (string memory) {
bytes memory s = new bytes(40);
for (uint i = 0; i < 20; i++) {
bytes1 b = bytes1(uint8(uint(uint160(x)) / (2 ** (8 * (19 - i)))));
Expand All @@ -269,17 +267,4 @@ contract upDevFunctionsConsumer is FunctionsClient, ConfirmedOwner {
if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
else return bytes1(uint8(b) + 0x57);
}

function isTrue(bytes memory input) public pure returns (bool) {
// Define the target byte pattern
bytes32 targetPattern = 0x0000000000000000000000000000000000000000000000000000000000000001;

// Convert the target pattern to bytes
bytes memory targetBytes = abi.encodePacked(targetPattern);

// Check if the input bytes are equal to the target pattern
return
input.length == targetBytes.length &&
keccak256(input) == keccak256(targetBytes);
}
}
Loading