Skip to content

Commit

Permalink
RELEASE: base superfluid faucet
Browse files Browse the repository at this point in the history
  • Loading branch information
sirpy committed Dec 24, 2024
1 parent bf53818 commit 8d8b135
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 68 deletions.
127 changes: 127 additions & 0 deletions contracts/fuseFaucet/SuperfluidFacuet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";

/**
* to be deployed on base to support superfluid airdrop to G$ users
*/
contract SuperfluidFaucet is
Initializable,
UUPSUpgradeable,
AccessControlUpgradeable
{
using SafeMathUpgradeable for uint256;

bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

uint256 public toppingAmount;
uint256 public maxAmountPerPeriod;
uint256 public toppingPeriod;

struct RecipientInfo {
uint256 lastWithdrawalPeriod;
uint256 totalWithdrawnThisPeriod;
}

mapping(address => RecipientInfo) public recipientInfo;

event WalletTopped(address recipient, uint256 amount);
event SettingsUpdated(
uint256 toppingAmount,
uint256 maxAmountPerPeriod,
uint256 toppingPeriod
);

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize(
uint256 _toppingAmount,
uint256 _maxAmountPerPeriod,
uint256 _toppingPeriod
) public initializer {
__AccessControl_init();
__UUPSUpgradeable_init();

_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(ADMIN_ROLE, msg.sender);
toppingAmount = _toppingAmount;
maxAmountPerPeriod = _maxAmountPerPeriod;
toppingPeriod = _toppingPeriod;
}

function updateSettings(
uint256 _toppingAmount,
uint256 _maxAmountPerPeriod,
uint256 _toppingPeriod
) external onlyRole(ADMIN_ROLE) {
toppingAmount = _toppingAmount;
maxAmountPerPeriod = _maxAmountPerPeriod;
toppingPeriod = _toppingPeriod;
emit SettingsUpdated(_toppingAmount, _maxAmountPerPeriod, _toppingPeriod);
}

function canTop(address recipient) public view returns (bool) {
if (recipient == address(0)) return false;
if (recipient.balance >= toppingAmount / 2) return false;

uint256 amountToSend = toppingAmount.sub(recipient.balance);
if (address(this).balance < amountToSend) return false;

uint256 currentPeriod = block.timestamp / toppingPeriod;
RecipientInfo storage info = recipientInfo[recipient];

if (currentPeriod > info.lastWithdrawalPeriod) {
return true; // New period, reset counters
}

if (info.totalWithdrawnThisPeriod.add(amountToSend) > maxAmountPerPeriod)
return false;

return true;
}

function topWallet(address payable recipient) external onlyRole(ADMIN_ROLE) {
require(canTop(recipient), "Recipient cannot be topped up");

uint256 currentPeriod = block.timestamp / toppingPeriod;
RecipientInfo storage info = recipientInfo[recipient];

if (currentPeriod > info.lastWithdrawalPeriod) {
info.totalWithdrawnThisPeriod = 0;
info.lastWithdrawalPeriod = currentPeriod;
}

uint256 amountToSend = toppingAmount.sub(recipient.balance);
require(
address(this).balance >= amountToSend,
"Insufficient contract balance for topping up"
);

info.totalWithdrawnThisPeriod = info.totalWithdrawnThisPeriod.add(
amountToSend
);

(bool success, ) = recipient.call{ value: amountToSend }("");
require(success, "Failed to send Ether");

emit WalletTopped(recipient, amountToSend);
}

receive() external payable {}

function withdraw() external onlyRole(DEFAULT_ADMIN_ROLE) {
uint256 balance = address(this).balance;
payable(msg.sender).transfer(balance);
}

function _authorizeUpgrade(
address newImplementation
) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
}
10 changes: 4 additions & 6 deletions contracts/utils/AdminWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ contract AdminWallet is
uint64 public maxDailyNewWallets;
uint64 public day;

ERC20 public gd;
ERC20 private gd_removed;

mapping(address => uint256) public lastGdBalance; // only top non whitelisted if active G$ users

event AdminsAdded(address payable[] indexed admins);
Expand Down Expand Up @@ -66,18 +67,13 @@ contract AdminWallet is
addAdmins(_admins);
}
if (msg.sender != _owner) revokeRole(DEFAULT_ADMIN_ROLE, msg.sender);
gd = ERC20(nameService.getAddress("GOODDOLLAR"));
}

modifier onlyOwner() {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "not owner");
_;
}

function upgrade() public onlyOwner {
gd = ERC20(nameService.getAddress("GOODDOLLAR"));
}

function getIdentity() public view returns (IIdentityV2) {
return IIdentityV2(nameService.getAddress("IDENTITY"));
}
Expand Down Expand Up @@ -236,6 +232,8 @@ contract AdminWallet is
* @param _user The address to transfer to
*/
function topWallet(address payable _user) public onlyAdmin reimburseGas {
ERC20 gd = ERC20(nameService.getAddress("GOODDOLLAR"));

uint256 gdBalance = gd.balanceOf(_user);
require(
getIdentity().isWhitelisted(_user) || gdBalance != lastGdBalance[_user],
Expand Down
60 changes: 21 additions & 39 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,20 @@ import { airdrop } from "./scripts/governance/airdropCalculationSorted";
import { airdrop as repAirdropRecover } from "./scripts/governance/airdropCalculationRecover";
import { airdrop as goodCheckpoint } from "./scripts/governance/goodCheckpointSorted";

import {
airdrop as gdxAirdrop,
airdropRecover as gdxAirdropRecover
} from "./scripts/gdx/gdxAirdropCalculation";
import { airdrop as gdxAirdrop, airdropRecover as gdxAirdropRecover } from "./scripts/gdx/gdxAirdropCalculation";
import { sumStakersGdRewards } from "./scripts/staking/stakersGdRewardsCalculation";
import { verify } from "./scripts/verify";
import { ethers } from "ethers";
import { fstat, readFileSync, writeFileSync } from "fs";
config();

const mnemonic =
process.env.MNEMONIC ||
"test test test test test test test test test test test junk";
const deployerPrivateKey =
process.env.PRIVATE_KEY || ethers.utils.hexZeroPad("0x11", 32);
const mnemonic = process.env.MNEMONIC || "test test test test test test test test test test test junk";
const deployerPrivateKey = process.env.PRIVATE_KEY || ethers.utils.hexZeroPad("0x11", 32);
const infura_api = process.env.INFURA_API;
const alchemy_key = process.env.ALCHEMY_KEY;
const etherscan_key = process.env.ETHERSCAN_KEY;
const celoscan_key = process.env.CELOSCAN_KEY;

const basescan_key = process.env.BASESCAN_KEY;
const ethplorer_key = process.env.ETHPLORER_KEY;

const MAINNET_URL = "https://mainnet.infura.io/v3/" + infura_api;
Expand Down Expand Up @@ -70,7 +64,8 @@ const hhconfig: HardhatUserConfig = {
apiKey: {
mainnet: etherscan_key,
celo: celoscan_key,
alfajores: celoscan_key
alfajores: celoscan_key,
base: basescan_key
},
customChains: [
{
Expand Down Expand Up @@ -99,9 +94,7 @@ const hhconfig: HardhatUserConfig = {

networks: {
hardhat: {
chainId: process.env.FORK_CHAIN_ID
? Number(process.env.FORK_CHAIN_ID)
: 4447,
chainId: process.env.FORK_CHAIN_ID ? Number(process.env.FORK_CHAIN_ID) : 4447,
allowUnlimitedContractSize: true,
accounts: {
accountsBalance: "10000000000000000000000000"
Expand Down Expand Up @@ -254,6 +247,12 @@ const hhconfig: HardhatUserConfig = {
gasPrice: 5000000000,
chainId: 42220
},
"development-base": {
accounts: { mnemonic },
url: "https://mainnet.base.org",
initialBaseFeePerGas: 0,
gasPrice: 8e6
},
gnosis: {
accounts: [deployerPrivateKey],
url: "https://rpc.gnosischain.com",
Expand All @@ -279,10 +278,7 @@ task("repAirdrop", "Calculates airdrop data and merkle tree")
const actions = airdrop(hre.ethers, ethplorer_key, etherscan_key);
switch (taskArgs.action) {
case "calculate":
return actions.collectAirdropData(
taskArgs.fusesnapshotblock,
taskArgs.ethsnapshotblock
);
return actions.collectAirdropData(taskArgs.fusesnapshotblock, taskArgs.ethsnapshotblock);
case "tree":
return actions.buildMerkleTree();
case "proof":
Expand All @@ -292,10 +288,7 @@ task("repAirdrop", "Calculates airdrop data and merkle tree")
}
});

task(
"repAirdropRecover",
"Calculates airdrop data and merkle tree after critical bug"
)
task("repAirdropRecover", "Calculates airdrop data and merkle tree after critical bug")
.addParam("action", "calculate/tree/proof")
.addOptionalPositionalParam("address", "proof for address")
.setAction(async (taskArgs, hre) => {
Expand Down Expand Up @@ -344,10 +337,7 @@ task("gdxAirdropRecover", "Calculates new airdrop data for recovery")
}
});

task(
"goodCheckpoint",
"Calculates good checkpoint data and merkle tree for GOOD sync"
)
task("goodCheckpoint", "Calculates good checkpoint data and merkle tree for GOOD sync")
.addParam("action", "calculate/tree/proof")
.addOptionalPositionalParam("address", "proof for address")
.setAction(async (taskArgs, hre) => {
Expand All @@ -364,17 +354,12 @@ task(
}
});

task("verifyjson", "verify contracts on etherscan").setAction(
async (taskArgs, hre) => {
return verify(hre);
}
);
task("verifyjson", "verify contracts on etherscan").setAction(async (taskArgs, hre) => {
return verify(hre);
});
export default hhconfig;

task(
"sumStakersGdRewards",
"Sums the GoodDollar reward for each staker"
).setAction(async (taskArgs, hre) => {
task("sumStakersGdRewards", "Sums the GoodDollar reward for each staker").setAction(async (taskArgs, hre) => {
const actions = sumStakersGdRewards(hre.ethers);
return actions.getStakersGdRewards();
});
Expand All @@ -387,10 +372,7 @@ task("cleanflat", "Cleans multiple SPDX and Pragma from flattened file")
});

// Remove every line started with "// SPDX-License-Identifier:"
flattened = flattened.replace(
/SPDX-License-Identifier:/gm,
"License-Identifier:"
);
flattened = flattened.replace(/SPDX-License-Identifier:/gm, "License-Identifier:");

flattened = `// SPDX-License-Identifier: MIXED\n\n${flattened}`;

Expand Down
5 changes: 5 additions & 0 deletions releases/deployment.json
Original file line number Diff line number Diff line change
Expand Up @@ -609,5 +609,10 @@
"DAI": "0xfcDB4564c18A9134002b9771816092C9693622e3",
"cDAI": "0x32EEce76C2C2e8758584A83Ee2F522D4788feA0f",
"COMP": "0x927b167526bAbB9be047421db732C663a0b77B11"
},
"development-base": {
"ProxyFactory": "0x5B22F5623ECB00E288539346F389E05f65e226ae",
"SuperfluidFaucet": "0x88d18B06E55b33B94578628fcB5a6e1D198b0e73",
"AdminWallet": "0x6672C998C49635aA6825Be355fF2e731f417B674"
}
}
35 changes: 20 additions & 15 deletions scripts/multichain-deploy/0_proxyFactory-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export const deployUniversalProxyFactory = async () => {
s: "0x2222222222222222222222222222222222222222222222222222222222222222"
};
//modify tx data a little so we get different contract address for different envs
if (name.includes("development-base")) {
deployTx.gasPrice = 7e7;
}
if (name.includes("staging")) {
deployTx.gasLimit = 892000;
} else if (name.includes("production")) {
Expand All @@ -34,36 +37,38 @@ export const deployUniversalProxyFactory = async () => {
const deployer = ethers.utils.recoverAddress(txHash, signer);
let [funder] = await ethers.getSigners();

let tx = await (
await funder.sendTransaction({
to: deployer,
value: ethers.BigNumber.from(deployTx.gasPrice).mul(deployTx.gasLimit)
})
).wait();
const curBalance = await ethers.provider.getBalance(deployer);
const deployCost = ethers.BigNumber.from(deployTx.gasPrice).mul(deployTx.gasLimit);

let tx = {};
if (curBalance.lt(deployCost)) {
tx = await (
await funder.sendTransaction({
to: deployer,
value: deployCost.sub(curBalance)
})
).wait();
}

if (isProduction) verifyProductionSigner(funder);

console.log({
fundingTx: tx.transactionHash,
deployer,
funder: funder.address,
deployerBalance: ethers.utils.formatUnits(
await ethers.provider.getBalance(deployer)
)
deployerBalance: ethers.utils.formatUnits(await ethers.provider.getBalance(deployer))
});
const signedTx = ethers.utils.serializeTransaction(deployTx, signer);
const result = await (await ethers.provider.sendTransaction(signedTx)).wait();
console.log({ result });
const proxyTx = await ethers.provider.sendTransaction(signedTx);
console.log({ proxyTx });
const result = await proxyTx.wait();
return ethers.getContractAt("ProxyFactory1967", result.contractAddress);
};

export const deployProxy = async (defaultAdmin = null) => {
let release: { [key: string]: any } = dao[network.name] || {};

if (
network.name.match(/production|staging|fuse|development/) &&
release.ProxyFactory
) {
if (network.name.match(/production|staging|fuse|development/) && release.ProxyFactory) {
throw new Error("ProxyFactory already exists for env");
}
// let [root] = await ethers.getSigners();
Expand Down
Loading

0 comments on commit 8d8b135

Please sign in to comment.