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

deploy & unittest script #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ jobs:
run: yarn compile

- name: Run unit tests
run: yarn test
run: yarn test script/unit_test.js
107 changes: 7 additions & 100 deletions contracts/NonfungibleTokenPositionDescriptor.sol
Original file line number Diff line number Diff line change
@@ -1,47 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
import '@uniswap/lib/contracts/libraries/SafeERC20Namer.sol';

import './libraries/ChainId.sol';
import './interfaces/INonfungiblePositionManager.sol';
import './interfaces/INonfungibleTokenPositionDescriptor.sol';
import './interfaces/IERC20Metadata.sol';
import './libraries/PoolAddress.sol';
import './libraries/NFTDescriptor.sol';
import './libraries/TokenRatioSortOrder.sol';
import '@uniswap/v3-core/contracts/UniswapV3Pool.sol';

//import "hardhat/console.sol";

/// @title Describes NFT token positions
/// @notice Produces a string containing the data URI for a JSON metadata string
contract NonfungibleTokenPositionDescriptor is INonfungibleTokenPositionDescriptor {
address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa;
address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;

address public immutable WETH9;
/// @dev A null-terminated string
bytes32 public immutable nativeCurrencyLabelBytes;

constructor(address _WETH9, bytes32 _nativeCurrencyLabelBytes) {
constructor(address _WETH9) {
WETH9 = _WETH9;
nativeCurrencyLabelBytes = _nativeCurrencyLabelBytes;
}

/// @notice Returns the native currency label as a string
function nativeCurrencyLabel() public view returns (string memory) {
uint256 len = 0;
while (len < 32 && nativeCurrencyLabelBytes[len] != 0) {
len++;
}
bytes memory b = new bytes(len);
for (uint256 i = 0; i < len; i++) {
b[i] = nativeCurrencyLabelBytes[i];
}
return string(b);
//bytes32 INIT_CODE_HASH = keccak256(abi.encodePacked(type(UniswapV3Pool).creationCode));
//console.log(uint256(INIT_CODE_HASH));
}

/// @inheritdoc INonfungibleTokenPositionDescriptor
Expand All @@ -51,74 +26,6 @@ contract NonfungibleTokenPositionDescriptor is INonfungibleTokenPositionDescript
override
returns (string memory)
{
(, , address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, , , , , ) =
positionManager.positions(tokenId);

IUniswapV3Pool pool =
IUniswapV3Pool(
PoolAddress.computeAddress(
positionManager.factory(),
PoolAddress.PoolKey({token0: token0, token1: token1, fee: fee})
)
);

bool _flipRatio = flipRatio(token0, token1, ChainId.get());
address quoteTokenAddress = !_flipRatio ? token1 : token0;
address baseTokenAddress = !_flipRatio ? token0 : token1;
(, int24 tick, , , , , ) = pool.slot0();

return
NFTDescriptor.constructTokenURI(
NFTDescriptor.ConstructTokenURIParams({
tokenId: tokenId,
quoteTokenAddress: quoteTokenAddress,
baseTokenAddress: baseTokenAddress,
quoteTokenSymbol: quoteTokenAddress == WETH9
? nativeCurrencyLabel()
: SafeERC20Namer.tokenSymbol(quoteTokenAddress),
baseTokenSymbol: baseTokenAddress == WETH9
? nativeCurrencyLabel()
: SafeERC20Namer.tokenSymbol(baseTokenAddress),
quoteTokenDecimals: IERC20Metadata(quoteTokenAddress).decimals(),
baseTokenDecimals: IERC20Metadata(baseTokenAddress).decimals(),
flipRatio: _flipRatio,
tickLower: tickLower,
tickUpper: tickUpper,
tickCurrent: tick,
tickSpacing: pool.tickSpacing(),
fee: fee,
poolAddress: address(pool)
})
);
}

function flipRatio(
address token0,
address token1,
uint256 chainId
) public view returns (bool) {
return tokenRatioPriority(token0, chainId) > tokenRatioPriority(token1, chainId);
}

function tokenRatioPriority(address token, uint256 chainId) public view returns (int256) {
if (token == WETH9) {
return TokenRatioSortOrder.DENOMINATOR;
}
if (chainId == 1) {
if (token == USDC) {
return TokenRatioSortOrder.NUMERATOR_MOST;
} else if (token == USDT) {
return TokenRatioSortOrder.NUMERATOR_MORE;
} else if (token == DAI) {
return TokenRatioSortOrder.NUMERATOR;
} else if (token == TBTC) {
return TokenRatioSortOrder.DENOMINATOR_MORE;
} else if (token == WBTC) {
return TokenRatioSortOrder.DENOMINATOR_MOST;
} else {
return 0;
}
}
return 0;
return "";
}
}
3 changes: 2 additions & 1 deletion contracts/libraries/PoolAddress.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ pragma solidity >=0.5.0;

/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
library PoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
//bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
bytes32 internal constant POOL_INIT_CODE_HASH = 0x9b06af945a15e497de0a98c56727a90114ae2d082285037c0045493ce98241aa;

/// @notice The identifying key of the pool
struct PoolKey {
Expand Down
2 changes: 2 additions & 0 deletions contracts/test/FromCore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '@uniswap/v3-core/contracts/UniswapV3Factory.sol';
import '@uniswap/v3-core/contracts/UniswapV3Pool.sol';
247 changes: 247 additions & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
require("@nomiclabs/hardhat-waffle");
require('hardhat-abi-exporter');
require("@nomicfoundation/hardhat-toolbox");
require("@nomicfoundation/hardhat-verify");


require('dotenv').config({ path: '.env' })
const fs = require("fs")

const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY

// 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 = {
mocha: {
timeout: 1000000
},
solidity: {
compilers: [
{
version: "0.7.6",
settings: {
//evmVersion: "london",
optimizer: {
enabled: true,
runs: 200
},
outputSelection: {
"*": {
"*": [
"abi",
"devdoc",
"metadata",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap",
"evm.methodIdentifiers"
],
"": ["ast"]
}
}
}
},
{
version: "0.4.24",
settings: {
evmVersion: "byzantium",
optimizer: {
enabled: true,
runs: 999999
},
}
}
]
},
paths: {
sources: "./contracts",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts"
},
defaultNetwork: 'hardhat',
networks: {
hardhat: {
blockGasLimit: 10000000,
accounts: [
{
privateKey: "10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef",
balance: "100000000000000000000000",
},
{
privateKey: "20abcdef10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef",
balance: "100000000000000000000000",
},
{
privateKey: "30abcdef10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef",
balance: "100000000000000000000000",
},
{
privateKey: "40abcdef10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef10abcdef",
balance: "100000000000000000000000",
},
]
},
dev: {
url: 'http://localhost:8545/',
network_id: "*",
},
blast_sepolia: {
url: 'https://sepolia.blast.io'
},
taiko_testnet: {
url: 'https://rpc.katla.taiko.xyz'
},
bitlayertestnet: {
url: 'https://testnet-rpc.bitlayer.org',
chainId: 200810
},
},
etherscan: {
apiKey: {
bitlayertestnet: "1234",
bitlayer: "1234"
},
customChains: [
{
network: "bitlayertestnet",
chainId: 200810,
urls: {
apiURL: "https://api-testnet.btrscan.com/scan/api",
browserURL: "https://testnet.btrscan.com/"
}
},
{
network: "blast_sepolia",
chainId: 168587773,
urls: {
apiURL: "https://api.routescan.io/v2/network/testnet/evm/168587773/etherscan",
browserURL: "https://testnet.blastscan.io"
}
},
{
network: "taiko_testnet",
chainId: 167008,
urls: {
apiURL: "https://blockscoutapi.katla.taiko.xyz/api?module=contract&action=verify",
browserURL: "https://explorer.katla.taiko.xyz/"
}
}

]
},
abiExporter: {
path: './abi/',
clear: false,
flat: false,
only: [],
}
};


function getSortedFiles(dependenciesGraph) {
const tsort = require("tsort")
const graph = tsort()

const filesMap = {}
const resolvedFiles = dependenciesGraph.getResolvedFiles()
resolvedFiles.forEach((f) => (filesMap[f.sourceName] = f))

for (const [from, deps] of dependenciesGraph.entries()) {
for (const to of deps) {
graph.add(to.sourceName, from.sourceName)
}
}

const topologicalSortedNames = graph.sort()

// If an entry has no dependency it won't be included in the graph, so we
// add them and then dedup the array
const withEntries = topologicalSortedNames.concat(resolvedFiles.map((f) => f.sourceName))

const sortedNames = [...new Set(withEntries)]
return sortedNames.map((n) => filesMap[n])
}

function getFileWithoutImports(resolvedFile) {
const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+)[\s\S]*?;\s*$/gm

return resolvedFile.content.rawContent.replace(IMPORT_SOLIDITY_REGEX, "").trim()
}

subtask("flat:get-flattened-sources", "Returns all contracts and their dependencies flattened")
.addOptionalParam("files", undefined, undefined, types.any)
.addOptionalParam("output", undefined, undefined, types.string)
.setAction(async ({ files, output }, { run }) => {
const dependencyGraph = await run("flat:get-dependency-graph", { files })
console.log(dependencyGraph)

let flattened = ""

if (dependencyGraph.getResolvedFiles().length === 0) {
return flattened
}

const sortedFiles = getSortedFiles(dependencyGraph)

let isFirst = true
for (const file of sortedFiles) {
if (!isFirst) {
flattened += "\n"
}
flattened += `// File ${file.getVersionedName()}\n`
flattened += `${getFileWithoutImports(file)}\n`

isFirst = false
}

// Remove every line started with "// SPDX-License-Identifier:"
flattened = flattened.replace(/SPDX-License-Identifier:/gm, "License-Identifier:")
flattened = flattened.replace(/pragma solidity [\^>=0-9.]*;\n/gm, "")
flattened = flattened.replace(/pragma solidity [\^><=0-9.\s]*;\n/gm, "")

flattened = `pragma solidity ^0.7.6;\n\n${flattened}`
flattened = `// SPDX-License-Identifier: MIT\n\n${flattened}`

// Remove every line started with "pragma experimental ABIEncoderV2;" except the first one
flattened = flattened.replace(/pragma experimental ABIEncoderV2;\n/gm, ((i) => (m) => (!i++ ? m : ""))(0))

flattened = flattened.trim()
if (output) {
console.log("Writing to", output)
fs.writeFileSync(output, flattened)
return ""
}
return flattened
})

subtask("flat:get-dependency-graph")
.addOptionalParam("files", undefined, undefined, types.any)
.setAction(async ({ files }, { run }) => {
const sourcePaths = files === undefined ? await run("compile:solidity:get-source-paths") : files.map((f) => fs.realpathSync(f))

const sourceNames = await run("compile:solidity:get-source-names", {
sourcePaths,
})

const dependencyGraph = await run("compile:solidity:get-dependency-graph", { sourceNames })

return dependencyGraph
})

task("flat", "Flattens and prints contracts and their dependencies")
.addOptionalVariadicPositionalParam("files", "The files to flatten", undefined, types.inputFile)
.addOptionalParam("output", "Specify the output file", undefined, types.string)
.setAction(async ({ files, output }, { run }) => {
console.log(
await run("flat:get-flattened-sources", {
files,
output,
})
)
})
Loading
Loading