Skip to content
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
8 changes: 7 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@
"Tokendecimals",
"zyfi",
"addrsSlot",
"NODLNS"
"NODLNS",
"unstake",
"unstakes",
"Unstaked",
"funder",
"Fundings",
"Reentrancy"
]
}
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,40 @@ N_LEVELS_URI_2=example.com \
forge script script/DeployMigrationNFT.s.sol --zksync --rpc-url https://sepolia.era.zksync.dev --zk-optimizer -i 1 --broadcast
```

## Deploying Staking contract
Allow users with at least 50,000 NODL (Dolphin level) to participate in a staking contract with the following characteristics:

### Functional Requirements
- Restricted access: Only users holding 50,000 NODL or more can stake.

- Staking cap: The contract accepts a maximum total of 5 million NODL per staker. Additionally, the contract has a global staking cap MAX_POOL_STAKE.

- Limited duration: Staking lasts the DURATION in seconds passed as a parameter.

- Guaranteed yield: Users receive a fixed reward, predefined in the contract, at the end of the staking period.

- Return: Once the staking period ends, both the staked tokens and the yield are returned to the user via a claim function.

### Deployment

```shell
GOV_ADDR=0x2D1941280530027B6bA80Af0e7bD8c2135783368 \
STAKE_TOKEN=0xb4B74C2BfeA877672B938E408Bae8894918fE41C \
MIN_STAKE=50000 \
MAX_TOTAL_STAKE=5000000 \
DURATION=3600 \
REWARD_RATE=12 \
REQUIRED_HOLDING_TOKEN=50000 \
npx hardhat deploy-zksync --script deploy_staking.dp.ts --network zkSyncSepoliaTestnet
```
- GOV_ADDR: Address of the governance contract.
- STAKE_TOKEN: Address of the token to stake.
- MIN_STAKE: Minimum amount of tokens a user can stake.
- MAX_TOTAL_STAKE: Maximum amount of tokens a user can stake.
- DURATION: Duration of the staking period in seconds.
- REWARD_RATE: Reward rate of the staking contract.
- REQUIRED_HOLDING_TOKEN: Minimum amount of tokens a user must hold to participate in the staking contract.

## Scripts

### Checking on bridging proposals
Expand Down Expand Up @@ -180,7 +214,27 @@ Verification on Etherscan is best done via the Solidity Json Input method as it

Use all these artifacts on the contract verification page on Etherscan for your given contract (open your contract on Etherscan, select `Contract` and the link starting with `Verify`). When prompted, enter the compiler versions, the license (we use BSD-3 Clause Clear). Then on the next page, enter your normalized JSON input file, and the contract constructor inputs.

## Deploying Staking contract

```shell
npx hardhat run --network zkSync deploy/deploy_staking.dp.ts
```

## Additional resources

- [L1 contracts](https://docs.zksync.io/zksync-era/environment/l1-contracts)
- [ZK stack addresses](https://docs.zksync.io/zk-stack/zk-chain-addresses)

## ZkSync CLI useful commands

# Approve

```shell
npx zksync-cli contract write --chain "zksync-sepolia" --contract "0xb4B74C2BfeA877672B938E408Bae8894918fE41C" --method "approve(address spender, uint256 amount)" --args "0x2D1941280530027B6bA80Af0e7bD8c2135783368" "1000000000000000000"
```

# Stake

```shell
npx zksync-cli contract write --chain "zksync-sepolia" --contract "0xb974a544128Bc7fAB3447D48cd6ad377D6F62EcF" --method "stake(uint256 amount)" --args "1000000000000000000"
```
65 changes: 65 additions & 0 deletions deploy/deploy_staking.dp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Provider, Wallet } from "zksync-ethers";
import { Deployer } from "@matterlabs/hardhat-zksync";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import "@matterlabs/hardhat-zksync-node/dist/type-extensions";
import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions";
import * as dotenv from "dotenv";

import { deployContract } from "./utils";

dotenv.config();
let CONTRACT_ADDRESS = "";
let SHOULD_DEPLOY = !CONTRACT_ADDRESS;

module.exports = async function (hre: HardhatRuntimeEnvironment) {
const tokenAddress = process.env.STAKE_TOKEN!;
const adminAddress = process.env.GOV_ADDR!;
const minStakeAmount = process.env.MIN_STAKE!;
const stakingPeriod = process.env.DURATION!;
const rewardRate = process.env.REWARD_RATE!;
const maxTotalStake = process.env.MAX_TOTAL_STAKE!;
const requiredHoldingToken = process.env.REQUIRED_HOLDING_TOKEN!;

const rpcUrl = hre.network.config.url!;
const provider = new Provider(rpcUrl);
const wallet = new Wallet(process.env.DEPLOYER_PRIVATE_KEY!, provider);
const deployer = new Deployer(hre, wallet);

const constructorArgs = [
tokenAddress,
(BigInt(requiredHoldingToken) * BigInt(1e18)).toString(),
Number(rewardRate),
(BigInt(minStakeAmount) * BigInt(1e18)).toString(),
(BigInt(maxTotalStake) * BigInt(1e18)).toString(),
Number(stakingPeriod),
adminAddress,
];

if (SHOULD_DEPLOY) {
const staking = await deployContract(deployer, "Staking", constructorArgs);
CONTRACT_ADDRESS = await staking.getAddress();
console.log(`Staking contract deployed at ${await staking.getAddress()}`);
console.log(
`!!! Do not forget to grant token approval to Staking contract at ${await staking.getAddress()} !!!`
);
}

if (CONTRACT_ADDRESS) {
console.log("Starting contract verification...");
try {
await hre.run("verify:verify", {
address: CONTRACT_ADDRESS,
contract: "src/Staking.sol:Staking",
constructorArguments: constructorArgs,
});
console.log("Contract verified successfully!");
} catch (error: any) {
if (error.message.includes("Contract source code already verified")) {
console.log("Contract is already verified!");
} else {
console.error("Error verifying contract:", error);
throw error;
}
}
}
};
Loading