Skip to content

Commit

Permalink
Merge pull request #18 from 1inch/SC-861-Migrate-to-new-hardhat-ethers
Browse files Browse the repository at this point in the history
[SC-861] Migrate to new hardhat-ethers
ZumZoom authored Aug 25, 2023
2 parents 1a16f44 + 67f0cdb commit a0ecf06
Showing 21 changed files with 2,184 additions and 7,362 deletions.
16 changes: 8 additions & 8 deletions contracts/CumulativeMerkleDrop.sol
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.15;
pragma abicoder v1;
pragma solidity 0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { SafeERC20, IERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
// import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

import "./interfaces/ICumulativeMerkleDrop.sol";
import { ICumulativeMerkleDrop } from "./interfaces/ICumulativeMerkleDrop.sol";

contract CumulativeMerkleDrop is Ownable, ICumulativeMerkleDrop {
using SafeERC20 for IERC20;
// using MerkleProof for bytes32[];

// solhint-disable-next-line immutable-vars-naming
address public immutable override token;

bytes32 public override merkleRoot;
@@ -33,15 +33,15 @@ contract CumulativeMerkleDrop is Ownable, ICumulativeMerkleDrop {
bytes32 expectedMerkleRoot,
bytes32[] calldata merkleProof
) external override {
require(merkleRoot == expectedMerkleRoot, "CMD: Merkle root was updated");
if (merkleRoot != expectedMerkleRoot) revert MerkleRootWasUpdated();

// Verify the merkle proof
bytes32 leaf = keccak256(abi.encodePacked(account, cumulativeAmount));
require(_verifyAsm(merkleProof, expectedMerkleRoot, leaf), "CMD: Invalid proof");
if (!_verifyAsm(merkleProof, expectedMerkleRoot, leaf)) revert InvalidProof();

// Mark it claimed
uint256 preclaimed = cumulativeClaimed[account];
require(preclaimed < cumulativeAmount, "CMD: Nothing to claim");
if (preclaimed >= cumulativeAmount) revert NothingToClaim();
cumulativeClaimed[account] = cumulativeAmount;

// Send the token
16 changes: 8 additions & 8 deletions contracts/CumulativeMerkleDrop128.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.15;
pragma abicoder v1;
pragma solidity 0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { SafeERC20, IERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";

import "./interfaces/ICumulativeMerkleDrop128.sol";
import { ICumulativeMerkleDrop128 } from "./interfaces/ICumulativeMerkleDrop128.sol";

contract CumulativeMerkleDrop128 is Ownable, ICumulativeMerkleDrop128 {
using SafeERC20 for IERC20;

// solhint-disable-next-line immutable-vars-naming
address public immutable override token;

bytes16 public override merkleRoot;
@@ -32,15 +32,15 @@ contract CumulativeMerkleDrop128 is Ownable, ICumulativeMerkleDrop128 {
bytes16 expectedMerkleRoot,
bytes calldata merkleProof
) external override {
require(merkleRoot == expectedMerkleRoot, "CMD: Merkle root was updated");
if (merkleRoot != expectedMerkleRoot) revert MerkleRootWasUpdated();

// Verify the merkle proof
bytes16 leaf = bytes16(keccak256((abi.encodePacked(salt, account, cumulativeAmount))));
require(_verifyAsm(merkleProof, expectedMerkleRoot, leaf), "CMD: Invalid proof");
if (!_verifyAsm(merkleProof, expectedMerkleRoot, leaf)) revert InvalidProof();

// Mark it claimed
uint256 preclaimed = cumulativeClaimed[account];
require(preclaimed < cumulativeAmount, "CMD: Nothing to claim");
if (preclaimed >= cumulativeAmount) revert NothingToClaim();
cumulativeClaimed[account] = cumulativeAmount;

// Send the token
21 changes: 11 additions & 10 deletions contracts/SignatureMerkleDrop128.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.15;
pragma abicoder v1;
pragma solidity 0.8.19;

import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import "@1inch/solidity-utils/contracts/libraries/ECDSA.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { SafeERC20, IERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import { ECDSA } from "@1inch/solidity-utils/contracts/libraries/ECDSA.sol";

import "./interfaces/ISignatureMerkleDrop128.sol";
import { ISignatureMerkleDrop128 } from "./interfaces/ISignatureMerkleDrop128.sol";

contract SignatureMerkleDrop128 is ISignatureMerkleDrop128, Ownable {
using Address for address payable;
using SafeERC20 for IERC20;

/* solhint-disable immutable-vars-naming */
address public immutable override token;
bytes16 public immutable override merkleRoot;
uint256 public immutable override depth;
/* solhint-enable immutable-vars-naming */

// This is a packed array of booleans.
mapping(uint256 => uint256) private _claimedBitMap;
@@ -38,7 +39,7 @@ contract SignatureMerkleDrop128 is ISignatureMerkleDrop128, Ownable {
// Verify the merkle proof.
bytes16 node = bytes16(keccak256(abi.encodePacked(account, amount)));
(bool valid, uint256 index) = _verifyAsm(merkleProof, merkleRoot, node);
require(valid, "MD: Invalid proof");
if (!valid) revert InvalidProof();
_invalidate(index);
IERC20(token).safeTransfer(receiver, amount);
_cashback();
@@ -73,7 +74,7 @@ contract SignatureMerkleDrop128 is ISignatureMerkleDrop128, Ownable {
uint256 claimedBitIndex = index & 0xff;
uint256 claimedWord = _claimedBitMap[claimedWordIndex];
uint256 newClaimedWord = claimedWord | (1 << claimedBitIndex);
require(claimedWord != newClaimedWord, "MD: Drop already claimed");
if (claimedWord == newClaimedWord) revert DropAlreadyClaimed();
_claimedBitMap[claimedWordIndex] = newClaimedWord;
}

4 changes: 4 additions & 0 deletions contracts/interfaces/ICumulativeMerkleDrop.sol
Original file line number Diff line number Diff line change
@@ -10,6 +10,10 @@ interface ICumulativeMerkleDrop {
// This event is triggered whenever a call to #claim succeeds.
event Claimed(address indexed account, uint256 amount);

error InvalidProof();
error NothingToClaim();
error MerkleRootWasUpdated();

// Returns the address of the token distributed by this contract.
function token() external view returns (address);
// Returns the merkle root of the merkle tree containing cumulative account balances available to claim.
4 changes: 4 additions & 0 deletions contracts/interfaces/ICumulativeMerkleDrop128.sol
Original file line number Diff line number Diff line change
@@ -10,6 +10,10 @@ interface ICumulativeMerkleDrop128 {
// This event is triggered whenever a call to #claim succeeds.
event Claimed(address indexed account, uint256 amount);

error InvalidProof();
error NothingToClaim();
error MerkleRootWasUpdated();

// Returns the address of the token distributed by this contract.
function token() external view returns (address);
// Returns the merkle root of the merkle tree containing cumulative account balances available to claim.
3 changes: 3 additions & 0 deletions contracts/interfaces/ISignatureMerkleDrop128.sol
Original file line number Diff line number Diff line change
@@ -5,6 +5,9 @@ pragma abicoder v1;

// Allows anyone to claim a token if they exist in a merkle root.
interface ISignatureMerkleDrop128 {
error InvalidProof();
error DropAlreadyClaimed();

// Returns the address of the token distributed by this contract.
function token() external view returns (address);
// Returns the merkle root of the merkle tree containing account balances available to claim.
2 changes: 1 addition & 1 deletion deploy/deploy_cumulative.js
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ module.exports = async ({ deployments, getNamedAccounts }) => {
);
await txn.wait();

console.log('CumulativeMerkleDrop deployed to:', cumulativeMerkleDrop.address);
console.log('CumulativeMerkleDrop deployed to:', await cumulativeMerkleDrop.getAddress());
};

module.exports.skip = async () => true;
10 changes: 5 additions & 5 deletions hardhat.config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
require('@nomiclabs/hardhat-ethers');
require('@nomiclabs/hardhat-etherscan');
require('@nomiclabs/hardhat-truffle5');
require('dotenv').config();
require('@nomicfoundation/hardhat-verify');
require('@nomicfoundation/hardhat-ethers');
require('@nomicfoundation/hardhat-chai-matchers');
require('hardhat-dependency-compiler');
require('hardhat-deploy');
require('hardhat-gas-reporter');
require('solidity-coverage');
require('dotenv').config();

const { networks, etherscan } = require('./hardhat.networks');

@@ -18,7 +18,7 @@ module.exports = {
runs: 1000000,
},
},
version: '0.8.15',
version: '0.8.19',
},
namedAccounts: {
deployer: {
48 changes: 23 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
@@ -8,37 +8,35 @@
},
"license": "MIT",
"dependencies": {
"@1inch/solidity-utils": "2.3.0",
"@openzeppelin/contracts": "4.6.0"
"@1inch/solidity-utils": "3.0.1",
"@openzeppelin/contracts": "4.9.3"
},
"devDependencies": {
"@metamask/eth-sig-util": "4.0.1",
"@nomiclabs/hardhat-ethers": "2.0.6",
"@nomiclabs/hardhat-etherscan": "3.0.3",
"@nomiclabs/hardhat-truffle5": "2.0.6",
"@nomiclabs/hardhat-web3": "2.0.0",
"@openzeppelin/test-helpers": "0.5.15",
"chai": "4.3.6",
"commander": "10.0.1",
"dotenv": "16.0.1",
"eslint": "8.16.0",
"eslint-config-standard": "17.0.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-n": "15.2.0",
"eslint-plugin-promise": "6.0.0",
"@metamask/eth-sig-util": "6.0.0",
"@nomicfoundation/hardhat-chai-matchers": "2.0.2",
"@nomicfoundation/hardhat-ethers": "3.0.4",
"@nomicfoundation/hardhat-verify": "1.1.1",
"chai": "4.3.7",
"commander": "11.0.0",
"dotenv": "16.3.1",
"eslint": "8.47.0",
"eslint-config-standard": "17.1.0",
"eslint-plugin-import": "2.28.0",
"eslint-plugin-n": "16.0.1",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-standard": "5.0.0",
"ethereumjs-wallet": "1.0.2",
"ethers": "5.6.7",
"hardhat": "2.13.0",
"ethereumjs-wallet": "^1.0.2",
"ethers": "6.7.1",
"hardhat": "2.17.1",
"hardhat-dependency-compiler": "1.1.3",
"hardhat-deploy": "0.11.10",
"hardhat-gas-reporter": "1.0.8",
"hardhat-deploy": "0.11.36",
"hardhat-gas-reporter": "1.0.9",
"keccak256": "1.0.6",
"merkletreejs": "0.2.31",
"merkletreejs": "0.3.10",
"qr-image": "3.2.0",
"rimraf": "3.0.2",
"solhint": "3.3.7",
"solidity-coverage": "0.8.2"
"rimraf": "5.0.1",
"solhint": "3.6.1",
"solidity-coverage": "0.8.4"
},
"scripts": {
"clean": "rimraf artifacts cache coverage coverage.json",
9 changes: 4 additions & 5 deletions src/gen_drop.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
const { MerkleTree } = require('merkletreejs');
const keccak256 = require('keccak256');
const fs = require('fs');
const { toBN } = require('../test/helpers/utils');

function findSortedIndex (self, h) {
return self.leaves.indexOf(h);
}

function makeDrop (wallets, amounts) {
const elements = wallets.map((w, i) => w + toBN(amounts[i]).toString(16, 64));
const elements = wallets.map((w, i) => w + BigInt(amounts[i]).toString(16).padStart(64, '0'));
const hashedElements = elements.map(keccak256).map(x => MerkleTree.bufferToHex(x));
const tree = new MerkleTree(elements, keccak256, { hashLeaves: true, sort: true });
const root = tree.getHexRoot();
@@ -26,11 +25,11 @@ const drop = makeDrop(Object.keys(json), Object.values(json));
console.log(
JSON.stringify({
merkleRoot: drop.root,
tokenTotal: '0x' + Object.values(json).map(toBN).reduce((a, b) => a.add(b), toBN('0')).toString(16),
tokenTotal: '0x' + Object.values(json).map(BigInt).reduce((a, b) => a + b, 0n).toString(16),
claims: Object.entries(json).map(([w, amount]) => ({
wallet: w,
amount: '0x' + toBN(amount).toString(16),
proof: drop.proofs[findSortedIndex(drop, MerkleTree.bufferToHex(keccak256(w + toBN(amount).toString(16, 64))))],
amount: '0x' + BigInt(amount).toString(16),
proof: drop.proofs[findSortedIndex(drop, MerkleTree.bufferToHex(keccak256(w + BigInt(amount).toString(16).padStart(64, '0'))))],
})).reduce((a, { wallet, amount, proof }) => {
a[wallet] = { amount, proof };
return a;
15 changes: 7 additions & 8 deletions src/gen_qr_drop.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const { ethers } = require('hardhat');
const { MerkleTree } = require('merkletreejs');
const keccak256 = require('keccak256');
const { toBN } = require('../test/helpers/utils');
const Wallet = require('ethereumjs-wallet').default;
const { promisify } = require('util');
const randomBytesAsync = promisify(require('crypto').randomBytes);
const { ether, BN } = require('@openzeppelin/test-helpers');
const qr = require('qr-image');
const fs = require('fs');
const path = require('path');
@@ -19,7 +18,7 @@ const flagSaveQr = true; // true - generate QR-codes, false - don't
const flagSaveLink = true; // true - generate links list, false - don't

// 10 - 1, 10 - 140, 20 - 140, 30 - 210, 40 - 140, 50 - 70
const AMOUNTS = [ether('1'), ether('10'), ether('20'), ether('30'), ether('40'), ether('50')];
const AMOUNTS = [ethers.parseEther('1'), ethers.parseEther('10'), ethers.parseEther('20'), ethers.parseEther('30'), ethers.parseEther('40'), ethers.parseEther('50')];
const COUNTS = [10, 140, 140, 210, 140, 70];

const VERSION = 27;
@@ -33,7 +32,7 @@ const validateRoot = ''; // merkle root
const PREFIX = 'https://app.1inch.io/#/1/qr?';

function makeDrop (wallets, amounts) {
const elements = wallets.map((w, i) => w + toBN(amounts[i]).toString(16, 64));
const elements = wallets.map((w, i) => w + amounts[i].toString(16).padStart(64, '0'));
const leaves = elements.map(keccak128).map(x => MerkleTree.bufferToHex(x));
const tree = new MerkleTree(leaves, keccak128, { sortPairs: true });
const root = tree.getHexRoot();
@@ -80,7 +79,7 @@ function saveQr (i, test, url) {

function verifyProof (wallet, amount, proof, root) {
const tree = new MerkleTree([], keccak128, { sortPairs: true });
const element = wallet + toBN(amount).toString(16, 64);
const element = wallet + amount.toString(16).padStart(64, '0');
const node = MerkleTree.bufferToHex(keccak128(element));
if (flagValidateOnly) {
console.log('root : ' + root);
@@ -106,15 +105,15 @@ function uriDecode (s, root) {

const key = kBuf.toString('hex').padStart(64, '0');
const wallet = Wallet.fromPrivateKey(Buffer.from(key, 'hex')).getAddressString();
const amount = new BN(aBuf.toString('hex'), 16).toString();
const amount = BigInt('0x' + aBuf.toString('hex'));

return verifyProof(wallet, amount, proof, root);
}

function genUrl (priv, amount, proof) {
const vBuf = Buffer.from([VERSION]);
const kBuf = Buffer.from(priv.substring(32), 'hex');
const aBuf = Buffer.from(toBN(amount).toString(16, 24), 'hex');
const aBuf = Buffer.from(amount.toString(16).padStart(24, '0'), 'hex');
const pBuf = Buffer.concat(proof.map(p => p.data));

const baseArgs = uriEncode(Buffer.concat([vBuf, kBuf, aBuf, pBuf]));
@@ -157,7 +156,7 @@ async function main () {
console.log('total:', amounts.length);
const drop = makeDrop(accounts, amounts);

console.log(drop.root, amounts.reduce((acc, v) => acc.add(v), toBN('0')).toString());
console.log(drop.root, amounts.reduce((acc, v) => acc + v, 0n).toString());

let indices = [];
for (let i = 0; i < amounts.length; i++) {
Loading

0 comments on commit a0ecf06

Please sign in to comment.