-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Buildspace tutorial - Domain name service on Polygon
- Loading branch information
0 parents
commit 978b904
Showing
10 changed files
with
28,015 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
node_modules | ||
.env | ||
coverage | ||
coverage.json | ||
typechain | ||
|
||
#Hardhat files | ||
cache | ||
artifacts |
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,15 @@ | ||
# Basic Sample Hardhat Project | ||
|
||
This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, a sample script that deploys that contract, and an example of a task implementation, which simply lists the available accounts. | ||
|
||
Try running some of the following tasks: | ||
|
||
```shell | ||
npx hardhat accounts | ||
npx hardhat compile | ||
npx hardhat clean | ||
npx hardhat test | ||
npx hardhat node | ||
node scripts/sample-script.js | ||
npx hardhat help | ||
``` |
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,143 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.10; | ||
|
||
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; | ||
import "@openzeppelin/contracts/utils/Counters.sol"; | ||
|
||
import { StringUtils } from "./libraries/StringUtils.sol"; | ||
import {Base64} from "./libraries/Base64.sol"; | ||
import "hardhat/console.sol"; | ||
|
||
contract Domains is ERC721URIStorage { | ||
// Magic given to us by OpenZeppelin to help us keep track of tokenIds. | ||
using Counters for Counters.Counter; | ||
Counters.Counter private _tokenIds; | ||
|
||
string public tld; | ||
|
||
// We'll be storing our NFT images on chain as SVGs | ||
string svgPartOne = '<svg xmlns="http://www.w3.org/2000/svg" width="270" height="270" fill="none"><path fill="url(#B)" d="M0 0h270v270H0z"/><defs><filter id="A" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="270" width="270"><feDropShadow dx="0" dy="1" stdDeviation="2" flood-opacity=".225" width="200%" height="200%"/></filter></defs><path d="M72.863 42.949c-.668-.387-1.426-.59-2.197-.59s-1.529.204-2.197.59l-10.081 6.032-6.85 3.934-10.081 6.032c-.668.387-1.426.59-2.197.59s-1.529-.204-2.197-.59l-8.013-4.721a4.52 4.52 0 0 1-1.589-1.616c-.384-.665-.594-1.418-.608-2.187v-9.31c-.013-.775.185-1.538.572-2.208a4.25 4.25 0 0 1 1.625-1.595l7.884-4.59c.668-.387 1.426-.59 2.197-.59s1.529.204 2.197.59l7.884 4.59a4.52 4.52 0 0 1 1.589 1.616c.384.665.594 1.418.608 2.187v6.032l6.85-4.065v-6.032c.013-.775-.185-1.538-.572-2.208a4.25 4.25 0 0 0-1.625-1.595L41.456 24.59c-.668-.387-1.426-.59-2.197-.59s-1.529.204-2.197.59l-14.864 8.655a4.25 4.25 0 0 0-1.625 1.595c-.387.67-.585 1.434-.572 2.208v17.441c-.013.775.185 1.538.572 2.208a4.25 4.25 0 0 0 1.625 1.595l14.864 8.655c.668.387 1.426.59 2.197.59s1.529-.204 2.197-.59l10.081-5.901 6.85-4.065 10.081-5.901c.668-.387 1.426-.59 2.197-.59s1.529.204 2.197.59l7.884 4.59a4.52 4.52 0 0 1 1.589 1.616c.384.665.594 1.418.608 2.187v9.311c.013.775-.185 1.538-.572 2.208a4.25 4.25 0 0 1-1.625 1.595l-7.884 4.721c-.668.387-1.426.59-2.197.59s-1.529-.204-2.197-.59l-7.884-4.59a4.52 4.52 0 0 1-1.589-1.616c-.385-.665-.594-1.418-.608-2.187v-6.032l-6.85 4.065v6.032c-.013.775.185 1.538.572 2.208a4.25 4.25 0 0 0 1.625 1.595l14.864 8.655c.668.387 1.426.59 2.197.59s1.529-.204 2.197-.59l14.864-8.655c.657-.394 1.204-.95 1.589-1.616s.594-1.418.609-2.187V55.538c.013-.775-.185-1.538-.572-2.208a4.25 4.25 0 0 0-1.625-1.595l-14.993-8.786z" fill="#fff"/><defs><linearGradient id="B" x1="0" y1="0" x2="270" y2="270" gradientUnits="userSpaceOnUse"><stop stop-color="#cb5eee"/><stop offset="1" stop-color="#0cd7e4" stop-opacity=".99"/></linearGradient></defs><text x="32.5" y="231" font-size="27" fill="#fff" filter="url(#A)" font-family="Plus Jakarta Sans,DejaVu Sans,Noto Color Emoji,Apple Color Emoji,sans-serif" font-weight="bold">'; | ||
string svgPartTwo = '</text></svg>'; | ||
|
||
address payable public owner; | ||
mapping(string => address) public domains; | ||
mapping(string => string) public records; | ||
mapping (uint => string) public names; | ||
|
||
error Unauthorized(); | ||
error AlreadyRegistered(); | ||
error InvalidName(string name); | ||
|
||
constructor(string memory _tld) payable ERC721("Grid Name Service", "GNS") { | ||
owner = payable(msg.sender); | ||
tld = _tld; | ||
console.log("%s name service deployed", _tld); | ||
} | ||
|
||
function register(string calldata name) public payable{ | ||
if (domains[name] != address(0)) revert AlreadyRegistered(); | ||
if (!valid(name)) revert InvalidName(name); | ||
|
||
uint256 _price = price(name); | ||
require(msg.value >= _price, "Not enough Matic paid"); | ||
|
||
// Combine the name passed into the function with the TLD | ||
string memory _name = string(abi.encodePacked(name, ".", tld)); | ||
// Create the SVG (image) for the NFT with the name | ||
string memory finalSvg = string(abi.encodePacked(svgPartOne, _name, svgPartTwo)); | ||
uint256 newRecordId = _tokenIds.current(); | ||
uint256 length = StringUtils.strlen(name); | ||
string memory strLen = Strings.toString(length); | ||
|
||
console.log("Registering %s.%s on the contract with tokenID %d", name, tld, newRecordId); | ||
|
||
// Create the JSON metadata of our NFT. We do this by combining strings and encoding as base64 | ||
string memory json = Base64.encode( | ||
bytes( | ||
string( | ||
abi.encodePacked( | ||
'{"name": "', | ||
_name, | ||
'", "description": "A domain on the Grid name service", "image": "data:image/svg+xml;base64,', | ||
Base64.encode(bytes(finalSvg)), | ||
'","length":"', | ||
strLen, | ||
'"}' | ||
) | ||
) | ||
) | ||
); | ||
|
||
string memory finalTokenUri = string( abi.encodePacked("data:application/json;base64,", json)); | ||
|
||
console.log("\n--------------------------------------------------------"); | ||
console.log("Final tokenURI", finalTokenUri); | ||
console.log("--------------------------------------------------------\n"); | ||
|
||
_safeMint(msg.sender, newRecordId); | ||
_setTokenURI(newRecordId, finalTokenUri); | ||
domains[name] = msg.sender; | ||
|
||
names[newRecordId] = name; | ||
_tokenIds.increment(); | ||
} | ||
|
||
// This function will give us the price of a domain based on length | ||
function price(string calldata name) public pure returns(uint) { | ||
uint len = StringUtils.strlen(name); | ||
require(len > 0); | ||
if (len == 3) { | ||
return 5 * 10**17; // 5 MATIC = 5 000 000 000 000 000 000 (18 decimals). We're going with 0.5 Matic cause the faucets don't give a lot | ||
} else if (len == 4) { | ||
return 3 * 10**17; // To charge smaller amounts, reduce the decimals. This is 0.3 | ||
} else { | ||
return 1 * 10**17; | ||
} | ||
} | ||
|
||
function getAddress(string calldata name) public view returns (address) { | ||
// Check that the owner is the transaction sender | ||
return domains[name]; | ||
} | ||
|
||
function setRecord(string calldata name, string calldata record) public { | ||
// Check that the owner is the transaction sender | ||
if (msg.sender != domains[name]) revert Unauthorized(); | ||
records[name] = record; | ||
} | ||
|
||
function getRecord(string calldata name) public view returns(string memory) { | ||
return records[name]; | ||
} | ||
|
||
modifier onlyOwner() { | ||
require(isOwner()); | ||
_; | ||
} | ||
|
||
function isOwner() public view returns (bool) { | ||
return msg.sender == owner; | ||
} | ||
|
||
function withdraw() public onlyOwner { | ||
uint amount = address(this).balance; | ||
|
||
(bool success, ) = msg.sender.call{value: amount}(""); | ||
require(success, "Failed to withdraw Matic"); | ||
} | ||
|
||
function getAllNames() public view returns (string[] memory) { | ||
console.log("Getting all names from contract"); | ||
string[] memory allNames = new string[](_tokenIds.current()); | ||
for (uint i = 0; i < _tokenIds.current(); i++) { | ||
allNames[i] = names[i]; | ||
console.log("Name for token %d is %s", i, allNames[i]); | ||
} | ||
|
||
return allNames; | ||
} | ||
|
||
function valid(string calldata name) public pure returns(bool) { | ||
return StringUtils.strlen(name) >= 3 && StringUtils.strlen(name) <= 10; | ||
} | ||
} |
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,78 @@ | ||
/** | ||
*Submitted for verification at Etherscan.io on 2021-09-05 | ||
*/ | ||
|
||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
/// [MIT License] | ||
/// @title Base64 | ||
/// @notice Provides a function for encoding some bytes in base64 | ||
/// @author Brecht Devos <[email protected]> | ||
library Base64 { | ||
bytes internal constant TABLE = | ||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
|
||
/// @notice Encodes some bytes to the base64 representation | ||
function encode(bytes memory data) internal pure returns (string memory) { | ||
uint256 len = data.length; | ||
if (len == 0) return ""; | ||
|
||
// multiply by 4/3 rounded up | ||
uint256 encodedLen = 4 * ((len + 2) / 3); | ||
|
||
// Add some extra buffer at the end | ||
bytes memory result = new bytes(encodedLen + 32); | ||
|
||
bytes memory table = TABLE; | ||
|
||
assembly { | ||
let tablePtr := add(table, 1) | ||
let resultPtr := add(result, 32) | ||
|
||
for { | ||
let i := 0 | ||
} lt(i, len) { | ||
|
||
} { | ||
i := add(i, 3) | ||
let input := and(mload(add(data, i)), 0xffffff) | ||
|
||
let out := mload(add(tablePtr, and(shr(18, input), 0x3F))) | ||
out := shl(8, out) | ||
out := add( | ||
out, | ||
and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF) | ||
) | ||
out := shl(8, out) | ||
out := add( | ||
out, | ||
and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF) | ||
) | ||
out := shl(8, out) | ||
out := add( | ||
out, | ||
and(mload(add(tablePtr, and(input, 0x3F))), 0xFF) | ||
) | ||
out := shl(224, out) | ||
|
||
mstore(resultPtr, out) | ||
|
||
resultPtr := add(resultPtr, 4) | ||
} | ||
|
||
switch mod(len, 3) | ||
case 1 { | ||
mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) | ||
} | ||
case 2 { | ||
mstore(sub(resultPtr, 1), shl(248, 0x3d)) | ||
} | ||
|
||
mstore(result, encodedLen) | ||
} | ||
|
||
return string(result); | ||
} | ||
} |
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,35 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Source: | ||
// https://github.com/ensdomains/ens-contracts/blob/master/contracts/ethregistrar/StringUtils.sol | ||
pragma solidity >=0.8.4; | ||
|
||
library StringUtils { | ||
/** | ||
* @dev Returns the length of a given string | ||
* | ||
* @param s The string to measure the length of | ||
* @return The length of the input string | ||
*/ | ||
function strlen(string memory s) internal pure returns (uint) { | ||
uint len; | ||
uint i = 0; | ||
uint bytelength = bytes(s).length; | ||
for(len = 0; i < bytelength; len++) { | ||
bytes1 b = bytes(s)[i]; | ||
if(b < 0x80) { | ||
i += 1; | ||
} else if (b < 0xE0) { | ||
i += 2; | ||
} else if (b < 0xF0) { | ||
i += 3; | ||
} else if (b < 0xF8) { | ||
i += 4; | ||
} else if (b < 0xFC) { | ||
i += 5; | ||
} else { | ||
i += 6; | ||
} | ||
} | ||
return len; | ||
} | ||
} |
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,28 @@ | ||
require("@nomiclabs/hardhat-waffle"); | ||
require('dotenv').config(); | ||
|
||
// This is a sample Hardhat task. To learn how to create your own go to | ||
// https://hardhat.org/guides/create-task.html | ||
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { | ||
const accounts = await hre.ethers.getSigners(); | ||
|
||
for (const account of accounts) { | ||
console.log(account.address); | ||
} | ||
}); | ||
|
||
// You need to export an object to set up your config | ||
// Go to https://hardhat.org/config/ to learn more | ||
|
||
/** | ||
* @type import('hardhat/config').HardhatUserConfig | ||
*/ | ||
module.exports = { | ||
solidity: "0.8.10", | ||
networks: { | ||
mumbai: { | ||
url: process.env.DEV_ALCHEMY_KEY, | ||
accounts: [process.env.PRIVATE_KEY], | ||
} | ||
} | ||
}; |
Oops, something went wrong.