From d2e7d37bf92ec99d0d6a883fcd251e57a4d03520 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Wed, 28 Jan 2026 14:07:40 +0200 Subject: [PATCH 1/2] add faucet for grants token --- contracts/grants/GrantsTokenFaucet.sol | 174 ++++++++ test/unit/grants/GrantsTokenFaucet.test.js | 497 +++++++++++++++++++++ 2 files changed, 671 insertions(+) create mode 100644 contracts/grants/GrantsTokenFaucet.sol create mode 100644 test/unit/grants/GrantsTokenFaucet.test.js diff --git a/contracts/grants/GrantsTokenFaucet.sol b/contracts/grants/GrantsTokenFaucet.sol new file mode 100644 index 00000000..1432695e --- /dev/null +++ b/contracts/grants/GrantsTokenFaucet.sol @@ -0,0 +1,174 @@ +pragma solidity 0.8.12; +// Copyright Ocean Protocol contributors +// SPDX-License-Identifier: Apache-2.0 + +import '../interfaces/IERC20.sol'; +import '../utils/Ownable.sol'; +import '../utils/SafeERC20.sol'; + +/** + * @title GrantsTokenFaucet + * @dev Faucet contract for GrantsToken that uses signature-based authentication. + * Backend validates user info and signs a message containing: userAddress, nonce, amount, userAddress. + * Users can claim tokens by providing a valid signature from the authorized signer. + */ +contract GrantsTokenFaucet is Ownable { + using SafeERC20 for IERC20; + + // The token that will be distributed + IERC20 public immutable token; + + // The address authorized to sign claim messages + address public signer; + + // Mapping from user address to their last used nonce + mapping(address => uint256) public userNonces; + + // Events + event TokensClaimed( + address indexed user, + uint256 nonce, + uint256 amount + ); + + event SignerChanged( + address indexed oldSigner, + address indexed newSigner + ); + + /** + * @dev Constructor + * @param _token Address of the GrantsToken contract + * @param _signer Address authorized to sign claim messages + */ + constructor(address _token, address _signer) { + require(_token != address(0), "GrantsTokenFaucet: invalid token address"); + require(_signer != address(0), "GrantsTokenFaucet: invalid signer address"); + + token = IERC20(_token); + signer = _signer; + } + + /** + * @dev Claim tokens using a signature from the authorized signer + * @param userAddress Address of the user claiming tokens + * @param nonce Nonce for this claim (must be greater than user's last used nonce) + * @param amount Amount of tokens to claim + * @param signature Signature from the authorized signer + */ + function claim( + address userAddress, + uint256 nonce, + uint256 amount, + bytes calldata signature + ) external { + require(userAddress != address(0), "GrantsTokenFaucet: invalid user address"); + require(amount > 0, "GrantsTokenFaucet: amount must be greater than zero"); + require(nonce > userNonces[userAddress], "GrantsTokenFaucet: nonce must be greater than last used nonce"); + + // Verify signature + // Include contract address to prevent cross-contract replay attacks + bytes32 messageHash = keccak256( + abi.encode(address(this), userAddress, nonce, amount) + ); + bytes32 ethSignedMessageHash = keccak256( + abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash) + ); + + address recoveredSigner = _recoverSigner(ethSignedMessageHash, signature); + require(recoveredSigner != address(0), "GrantsTokenFaucet: invalid signature recovery"); + require(recoveredSigner == signer, "GrantsTokenFaucet: invalid signature"); + + // Update user nonce + userNonces[userAddress] = nonce; + emit TokensClaimed(userAddress, nonce, amount); + // Transfer tokens to user + token.safeTransfer(userAddress, amount); + + + } + + /** + * @dev Change the authorized signer (only owner) + * @param newSigner Address of the new authorized signer + */ + function setSigner(address newSigner) external onlyOwner { + require(newSigner != address(0), "GrantsTokenFaucet: invalid signer address"); + require(newSigner != signer, "GrantsTokenFaucet: signer is already set to this address"); + + address oldSigner = signer; + signer = newSigner; + + emit SignerChanged(oldSigner, newSigner); + } + + /** + * @dev Get the authorized signer address + * @return The address of the authorized signer + */ + function getSigner() external view returns (address) { + return signer; + } + + /** + * @dev Get the last used nonce for a user + * @param user Address of the user + * @return The last used nonce + */ + function getUserNonce(address user) external view returns (uint256) { + return userNonces[user]; + } + + /** + * @dev Recover signer address from signature + * @param hash The message hash + * @param signature The signature + * @return The recovered signer address + */ + function _recoverSigner(bytes32 hash, bytes memory signature) internal pure returns (address) { + require(signature.length == 65, "GrantsTokenFaucet: invalid signature length"); + + bytes32 r; + bytes32 s; + uint8 v; + + assembly { + r := mload(add(signature, 32)) + s := mload(add(signature, 64)) + v := and(mload(add(signature, 65)), 255) + } + + // Prevent signature malleability: s must be in the lower half of the secp256k1 curve order + require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + "GrantsTokenFaucet: invalid signature s value"); + + // Handle v values 0-1 (EIP-155) and 27-28 (legacy) + if (v < 27) { + v += 27; + } + + require(v == 27 || v == 28, "GrantsTokenFaucet: invalid signature v value"); + + address recovered = ecrecover(hash, v, r, s); + require(recovered != address(0), "GrantsTokenFaucet: invalid signature recovery"); + + return recovered; + } + + /** + * @dev Emergency function to withdraw tokens (only owner) + * Useful if tokens need to be recovered or contract needs to be upgraded + * @param amount Amount of tokens to withdraw + */ + function withdrawTokens(uint256 amount) external onlyOwner { + token.safeTransfer(owner(), amount); + } + + /** + * @dev Emergency function to withdraw all tokens (only owner) + */ + function withdrawAllTokens() external onlyOwner { + uint256 balance = token.balanceOf(address(this)); + token.safeTransfer(owner(), balance); + } +} diff --git a/test/unit/grants/GrantsTokenFaucet.test.js b/test/unit/grants/GrantsTokenFaucet.test.js new file mode 100644 index 00000000..f6a70faf --- /dev/null +++ b/test/unit/grants/GrantsTokenFaucet.test.js @@ -0,0 +1,497 @@ +/* eslint-env mocha */ +/* global artifacts, contract, web3, it, beforeEach, before */ +const hre = require("hardhat"); +const ethers = hre.ethers; +const { expectRevert, expectEvent } = require("@openzeppelin/test-helpers"); +const { getEventFromTx } = require("../../helpers/utils"); + +// Helper function to create values with 6 decimals +function parseTokens(amount) { + return ethers.utils.parseUnits(amount.toString(), 6); +} + +// Helper function to sign a message for the faucet (similar to ERC20Template.test.js) +// Uses ethers signer.signMessage which works with Hardhat (web3.eth.sign requires external node) +async function signFaucetMessage(signer, faucetAddress, userAddress, nonce, amount) { + // Create the message hash: keccak256(abi.encode(faucetAddress, userAddress, nonce, amount)) + // Updated to match contract: includes contract address to prevent cross-contract replay + // Use defaultAbiCoder.encode to match contract's abi.encode exactly + const encoded = ethers.utils.defaultAbiCoder.encode( + ["address", "address", "uint256", "uint256"], + [faucetAddress, userAddress, nonce, amount] + ); + const messageHash = ethers.utils.keccak256(encoded); + + // signer.signMessage automatically adds "\x19Ethereum Signed Message:\n32" prefix (like web3.eth.sign) + // This creates: keccak256("\x19Ethereum Signed Message:\n32" + messageHash) + // Which matches exactly what the contract creates as ethSignedMessageHash + const signedMessage = await signer.signMessage(ethers.utils.arrayify(messageHash)); + + // Extract r, s, v from signature (same pattern as ERC20Template.test.js) + const sig = signedMessage.substr(2); // remove 0x + const r = '0x' + sig.slice(0, 64); + const s = '0x' + sig.slice(64, 128); + const v = '0x' + sig.slice(128, 130); + + // Convert to bytes format expected by contract (65 bytes: 32 + 32 + 1) + const signature = ethers.utils.concat([ + r, + s, + v + ]); + + return signature; +} + +describe("GrantsTokenFaucet", () => { + let grantsToken; + let faucet; + let owner; + let signer; + let user; + let other; + let assert; + let expect; + + const INITIAL_SUPPLY = parseTokens("1000000"); // 1 million tokens + const TOKEN_CAP = parseTokens("10000000"); // 10 million tokens + const FAUCET_AMOUNT = parseTokens("100000"); // 100k tokens for faucet + + before("setup test helpers", async function () { + // Dynamic import of chai to handle ESM module + const chai = await import("chai"); + assert = chai.assert; + expect = chai.expect; + }); + + beforeEach("deploy contracts", async () => { + [owner, signer, user, other] = await ethers.getSigners(); + + // Deploy GrantsToken + const GrantsToken = await ethers.getContractFactory("GrantsToken"); + grantsToken = await GrantsToken.deploy(INITIAL_SUPPLY, TOKEN_CAP); + await grantsToken.deployed(); + + // Deploy GrantsTokenFaucet + const GrantsTokenFaucet = await ethers.getContractFactory("GrantsTokenFaucet"); + faucet = await GrantsTokenFaucet.deploy(grantsToken.address, signer.address); + await faucet.deployed(); + + // Transfer tokens to faucet + await grantsToken.transfer(faucet.address, FAUCET_AMOUNT); + }); + + describe("Deployment", () => { + it("should set correct token address", async () => { + const tokenAddress = await faucet.token(); + assert.equal(tokenAddress, grantsToken.address); + }); + + it("should set correct signer address", async () => { + const signerAddress = await faucet.getSigner(); + assert.equal(signerAddress, signer.address); + }); + + it("should set owner correctly", async () => { + const contractOwner = await faucet.owner(); + assert.equal(contractOwner, owner.address); + }); + + it("should revert if token address is zero", async () => { + const GrantsTokenFaucet = await ethers.getContractFactory("GrantsTokenFaucet"); + await expectRevert( + GrantsTokenFaucet.deploy( + "0x0000000000000000000000000000000000000000", + signer.address + ), + "GrantsTokenFaucet: invalid token address" + ); + }); + + it("should revert if signer address is zero", async () => { + const GrantsTokenFaucet = await ethers.getContractFactory("GrantsTokenFaucet"); + await expectRevert( + GrantsTokenFaucet.deploy( + grantsToken.address, + "0x0000000000000000000000000000000000000000" + ), + "GrantsTokenFaucet: invalid signer address" + ); + }); + }); + + describe("Claim", () => { + it("should allow user to claim tokens with valid signature", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + const initialBalance = await grantsToken.balanceOf(user.address); + + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + const tx = await faucet.connect(user).claim(user.address, nonce, amount, signature); + const txReceipt = await tx.wait(); + + const finalBalance = await grantsToken.balanceOf(user.address); + assert.isTrue(finalBalance.eq(initialBalance.add(amount))); + + // Check event + const event = getEventFromTx(txReceipt, "TokensClaimed"); + assert(event, "Cannot find TokensClaimed event"); + assert.equal(event.args.user, user.address); + assert.isTrue(event.args.nonce.eq(nonce)); + assert.isTrue(event.args.amount.eq(amount)); + }); + + it("should update user nonce after claim", async () => { + const nonce1 = 1; + const nonce2 = 2; + const amount = parseTokens("1000"); + + // First claim + const signature1 = await signFaucetMessage(signer, faucet.address, user.address, nonce1, amount); + await faucet.connect(user).claim(user.address, nonce1, amount, signature1); + + const userNonce1 = await faucet.userNonces(user.address); + assert.isTrue(userNonce1.eq(nonce1)); + + // Second claim with higher nonce + const signature2 = await signFaucetMessage(signer, faucet.address, user.address, nonce2, amount); + await faucet.connect(user).claim(user.address, nonce2, amount, signature2); + + const userNonce2 = await faucet.userNonces(user.address); + assert.isTrue(userNonce2.eq(nonce2)); + }); + + it("should allow multiple claims with increasing nonces", async () => { + const amount = parseTokens("1000"); + const initialBalance = await grantsToken.balanceOf(user.address); + + // Claim with nonce 1 + const signature1 = await signFaucetMessage(signer, faucet.address, user.address, 1, amount); + await faucet.connect(user).claim(user.address, 1, amount, signature1); + + // Claim with nonce 2 + const signature2 = await signFaucetMessage(signer, faucet.address, user.address, 2, amount); + await faucet.connect(user).claim(user.address, 2, amount, signature2); + + // Claim with nonce 3 + const signature3 = await signFaucetMessage(signer, faucet.address, user.address, 3, amount); + await faucet.connect(user).claim(user.address, 3, amount, signature3); + + const finalBalance = await grantsToken.balanceOf(user.address); + assert.isTrue(finalBalance.eq(initialBalance.add(amount.mul(3)))); + }); + + it("should revert if nonce is not greater than last used nonce", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + // First claim succeeds + await faucet.connect(user).claim(user.address, nonce, amount, signature); + + // Second claim with same nonce should fail + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, signature), + "GrantsTokenFaucet: nonce must be greater than last used nonce" + ); + }); + + it("should revert if nonce is less than last used nonce", async () => { + const amount = parseTokens("1000"); + + // Claim with nonce 5 + const signature5 = await signFaucetMessage(signer, faucet.address, user.address, 5, amount); + await faucet.connect(user).claim(user.address, 5, amount, signature5); + + // Try to claim with nonce 3 (should fail) + const signature3 = await signFaucetMessage(signer, faucet.address, user.address, 3, amount); + await expectRevert( + faucet.connect(user).claim(user.address, 3, amount, signature3), + "GrantsTokenFaucet: nonce must be greater than last used nonce" + ); + }); + + it("should revert if signature is invalid", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + + // Sign with wrong signer + const wrongSignature = await signFaucetMessage(other, faucet.address, user.address, nonce, amount); + + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, wrongSignature), + "GrantsTokenFaucet: invalid signature" + ); + }); + + it("should revert if signature is for different user", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + + // Sign for different user + const signature = await signFaucetMessage(signer, faucet.address, other.address, nonce, amount); + + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, signature), + "GrantsTokenFaucet: invalid signature" + ); + }); + + it("should revert if signature is for different nonce", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + + // Sign for different nonce + const signature = await signFaucetMessage(signer, faucet.address, user.address, 2, amount); + + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, signature), + "GrantsTokenFaucet: invalid signature" + ); + }); + + it("should revert if signature is for different amount", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + const differentAmount = parseTokens("2000"); + + // Sign for different amount + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, differentAmount); + + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, signature), + "GrantsTokenFaucet: invalid signature" + ); + }); + + it("should revert if amount is zero", async () => { + const nonce = 1; + const amount = 0; + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, signature), + "GrantsTokenFaucet: amount must be greater than zero" + ); + }); + + it("should revert if user address is zero", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + await expectRevert( + faucet.connect(user).claim( + "0x0000000000000000000000000000000000000000", + nonce, + amount, + signature + ), + "GrantsTokenFaucet: invalid user address" + ); + }); + + it("should revert if signature length is invalid", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + const invalidSignature = "0x1234"; // Too short + + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, invalidSignature), + "GrantsTokenFaucet: invalid signature length" + ); + }); + + it("should allow different users to claim independently", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + + // User 1 claims + const signature1 = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + await faucet.connect(user).claim(user.address, nonce, amount, signature1); + + // User 2 claims with same nonce (should work, nonces are per-user) + const signature2 = await signFaucetMessage(signer, faucet.address, other.address, nonce, amount); + await faucet.connect(other).claim(other.address, nonce, amount, signature2); + + const user1Nonce = await faucet.userNonces(user.address); + const user2Nonce = await faucet.userNonces(other.address); + assert.isTrue(user1Nonce.eq(nonce)); + assert.isTrue(user2Nonce.eq(nonce)); + }); + + it("should allow anyone to call claim for any user", async () => { + const nonce = 1; + const amount = parseTokens("1000"); + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + // Other user calls claim for user + await faucet.connect(other).claim(user.address, nonce, amount, signature); + + const balance = await grantsToken.balanceOf(user.address); + assert.isTrue(balance.eq(amount)); + }); + }); + + describe("setSigner", () => { + it("should allow owner to change signer", async () => { + const newSigner = other; + const tx = await faucet.setSigner(newSigner.address); + const txReceipt = await tx.wait(); + + const currentSigner = await faucet.getSigner(); + assert.equal(currentSigner, newSigner.address); + + // Check event + const event = getEventFromTx(txReceipt, "SignerChanged"); + assert(event, "Cannot find SignerChanged event"); + assert.equal(event.args.oldSigner, signer.address); + assert.equal(event.args.newSigner, newSigner.address); + }); + + it("should allow claims with new signer after change", async () => { + const newSigner = other; + await faucet.setSigner(newSigner.address); + + const nonce = 1; + const amount = parseTokens("1000"); + const signature = await signFaucetMessage(newSigner, faucet.address, user.address, nonce, amount); + + await faucet.connect(user).claim(user.address, nonce, amount, signature); + + const balance = await grantsToken.balanceOf(user.address); + assert.isTrue(balance.eq(amount)); + }); + + it("should reject claims with old signer after change", async () => { + const newSigner = other; + await faucet.setSigner(newSigner.address); + + const nonce = 1; + const amount = parseTokens("1000"); + const oldSignature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, oldSignature), + "GrantsTokenFaucet: invalid signature" + ); + }); + + it("should revert if non-owner tries to change signer", async () => { + await expectRevert( + faucet.connect(user).setSigner(other.address), + "Ownable: caller is not the owner" + ); + }); + + it("should revert if new signer is zero address", async () => { + await expectRevert( + faucet.setSigner("0x0000000000000000000000000000000000000000"), + "GrantsTokenFaucet: invalid signer address" + ); + }); + + it("should revert if new signer is same as current signer", async () => { + await expectRevert( + faucet.setSigner(signer.address), + "GrantsTokenFaucet: signer is already set to this address" + ); + }); + }); + + describe("getUserNonce", () => { + it("should return zero for new user", async () => { + const nonce = await faucet.getUserNonce(user.address); + assert.isTrue(nonce.eq(0)); + }); + + it("should return correct nonce after claim", async () => { + const nonce = 5; + const amount = parseTokens("1000"); + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + await faucet.connect(user).claim(user.address, nonce, amount, signature); + + const userNonce = await faucet.getUserNonce(user.address); + assert.isTrue(userNonce.eq(nonce)); + }); + }); + + describe("withdrawTokens", () => { + it("should allow owner to withdraw tokens", async () => { + const withdrawAmount = parseTokens("10000"); + const initialOwnerBalance = await grantsToken.balanceOf(owner.address); + + await faucet.withdrawTokens(withdrawAmount); + + const finalOwnerBalance = await grantsToken.balanceOf(owner.address); + assert.isTrue(finalOwnerBalance.eq(initialOwnerBalance.add(withdrawAmount))); + }); + + it("should revert if non-owner tries to withdraw", async () => { + const withdrawAmount = parseTokens("10000"); + await expectRevert( + faucet.connect(user).withdrawTokens(withdrawAmount), + "Ownable: caller is not the owner" + ); + }); + }); + + describe("withdrawAllTokens", () => { + it("should allow owner to withdraw all tokens", async () => { + const initialOwnerBalance = await grantsToken.balanceOf(owner.address); + const faucetBalance = await grantsToken.balanceOf(faucet.address); + + await faucet.withdrawAllTokens(); + + const finalOwnerBalance = await grantsToken.balanceOf(owner.address); + const finalFaucetBalance = await grantsToken.balanceOf(faucet.address); + + assert.isTrue(finalOwnerBalance.eq(initialOwnerBalance.add(faucetBalance))); + assert.isTrue(finalFaucetBalance.eq(0)); + }); + + it("should revert if non-owner tries to withdraw all", async () => { + await expectRevert( + faucet.connect(user).withdrawAllTokens(), + "Ownable: caller is not the owner" + ); + }); + }); + + describe("Edge Cases", () => { + it("should handle large nonce values", async () => { + const nonce = ethers.BigNumber.from("2").pow(256).sub(1); + const amount = parseTokens("1000"); + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + await faucet.connect(user).claim(user.address, nonce, amount, signature); + + const userNonce = await faucet.getUserNonce(user.address); + assert.isTrue(userNonce.eq(nonce)); + }); + + it("should handle large amount values", async () => { + const nonce = 1; + const amount = FAUCET_AMOUNT; // Use all faucet balance + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + await faucet.connect(user).claim(user.address, nonce, amount, signature); + + const balance = await grantsToken.balanceOf(user.address); + assert.isTrue(balance.eq(amount)); + }); + + it("should revert if claiming more than faucet balance", async () => { + const nonce = 1; + const amount = FAUCET_AMOUNT.add(1); // More than faucet has + const signature = await signFaucetMessage(signer, faucet.address, user.address, nonce, amount); + + await expectRevert( + faucet.connect(user).claim(user.address, nonce, amount, signature), + "ERC20: transfer amount exceeds balance" + ); + }); + }); +}); From 38457dfa926f2d4f3ca8dacf16c5b6378b8bf732 Mon Sep 17 00:00:00 2001 From: alexcos20 Date: Thu, 5 Feb 2026 07:33:00 +0200 Subject: [PATCH 2/2] deploy compy & faucet --- addresses/address.json | 8 ++- scripts/deploy_grants.js | 20 +++++- scripts/deploy_grants_faucet.js | 107 ++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 scripts/deploy_grants_faucet.js diff --git a/addresses/address.json b/addresses/address.json index a309d2db..954e974f 100755 --- a/addresses/address.json +++ b/addresses/address.json @@ -309,7 +309,9 @@ "EnterpriseFeeCollector": "0x8c98ea273bA22327F896Aa1a1a46E1BFf56e9b1D", "FixedPriceEnterprise": "0xfa48673a7C36A2A768f89AC1ee8C355D5c367B02", "AccessListFactory": "0x43eC0a34E1b70C7f8E579ab866F37642777727E7", - "EnterpriseEscrow": "0x49E35cd2bAE043Abd9074B6e5a649a5AdEB05C33" + "EnterpriseEscrow": "0x49E35cd2bAE043Abd9074B6e5a649a5AdEB05C33", + "COMPY":"0x973e69303259B0c2543a38665122b773D28405fB", + "COMPYFaucet":"0x3EFDD8f728c8e774aB81D14d0B2F07a8238960f4" }, "oasis_sapphire": { "chainId": 23294, @@ -416,6 +418,8 @@ "BatchPayments": "0xFe7967A5176fDAFa8DE109b3507016B885a82D6e", "OPFCommunityFeeCollectorCompute": "0x7b0576CF01E868bce46cca91b2a8E674141b0355", "Escrow": "0xf0c7A31D7Ee26bEBfb4BAD8e37490bEadE3F846f", - "AccessListFactory": "0xE5aa2C9B551aFcA4C0A98BB3B37D7A43084d0a66" + "AccessListFactory": "0xE5aa2C9B551aFcA4C0A98BB3B37D7A43084d0a66", + "COMPY":"0x298f163244e0c8cc9316D6E97162e5792ac5d410", + "COMPYFaucet":"0x23A8b2D7176485a6349e4830605F323f31019333" } } \ No newline at end of file diff --git a/scripts/deploy_grants.js b/scripts/deploy_grants.js index 8f0ea0f3..9b50613c 100644 --- a/scripts/deploy_grants.js +++ b/scripts/deploy_grants.js @@ -42,9 +42,25 @@ async function main() { let RouterAddress = null; gasLimit = 6500000; gasPrice = ethers.utils.parseUnits("0.08", "gwei"); - const networkName = "base"; + let networkName = null; const grantsOwner = "0x09b575B5eC7Fff24cbccC092DE9E36eADdDbEe71"; - + switch (networkDetails.chainId) { + case 11155111: + networkName = "sepolia"; + gasPrice = ethers.utils.parseUnits("1.1", "gwei"); + gasLimit = 6000000; + break; + case 8453: + networkName = "base"; + gasPrice = ethers.utils.parseUnits("0.08", "gwei"); + gasLimit = 20000000; + // grantsTokenAddress = "0x09b575B5eC7Fff24cbccC092DE9E36eADdDbEe71"; + break; + } + if (!networkName) { + console.error("Invalid network. Aborting.."); + return null; + } let options; if (gasPrice) { options = { gasLimit: gasLimit, gasPrice: gasPrice }; diff --git a/scripts/deploy_grants_faucet.js b/scripts/deploy_grants_faucet.js new file mode 100644 index 00000000..dfbb7e33 --- /dev/null +++ b/scripts/deploy_grants_faucet.js @@ -0,0 +1,107 @@ +// We require the Hardhat Runtime Environment explicitly here. This is optional +// but useful for running the script in a standalone fashion through `node