{"id":28178488,"url":"https://github.com/slycompiler/nft-staking-solidity","last_synced_at":"2026-05-16T06:38:19.855Z","repository":{"id":333472672,"uuid":"797722391","full_name":"slycompiler/nft-staking-solidity","owner":"slycompiler","description":"An NFT Staking contract deployed using thirdweb deploy, where users stake their ERC721 tokens and earn ERC20 tokens in return!","archived":false,"fork":false,"pushed_at":"2024-05-08T11:47:39.000Z","size":112,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-30T00:35:51.780Z","etag":null,"topics":["erc721-tokens","solidity","staking","thirdweb-deploy","typescirpt"],"latest_commit_sha":null,"homepage":"https://nft-staking-contract.thirdweb-example.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/slycompiler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-05-08T11:46:21.000Z","updated_at":"2024-09-10T13:26:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/slycompiler/nft-staking-solidity","commit_stats":null,"previous_names":["slycompiler/nft-staking-solidity","hardworking-toptal-dev/nft-staking-solidity"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/slycompiler/nft-staking-solidity","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slycompiler%2Fnft-staking-solidity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slycompiler%2Fnft-staking-solidity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slycompiler%2Fnft-staking-solidity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slycompiler%2Fnft-staking-solidity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slycompiler","download_url":"https://codeload.github.com/slycompiler/nft-staking-solidity/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slycompiler%2Fnft-staking-solidity/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33092832,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T04:41:52.686Z","status":"ssl_error","status_checked_at":"2026-05-16T04:41:52.009Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["erc721-tokens","solidity","staking","thirdweb-deploy","typescirpt"],"created_at":"2025-05-16T01:12:35.834Z","updated_at":"2026-05-16T06:38:19.840Z","avatar_url":"https://github.com/slycompiler.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NFT Staking App\r\n\r\n**Disclaimer**: This smart contract is not tested or audited by thirdweb. It is not intended for production use cases.\r\n\r\n## Introduction\r\n\r\nThis example demonstrates a use of several thirdweb tools to create an NFT Staking application. In this example, users can stake their ERC721 NFTs and earn ERC20 tokens as a reward. It combines:\r\n\r\n1. [thirdweb's NFT Drop Contract](https://portal.thirdweb.com/pre-built-contracts/nft-drop)\r\n2. [thirdweb's Token Contract](https://portal.thirdweb.com/pre-built-contracts/token)\r\n3. A modified version of this [NFT Staking Smart Contract](https://github.com/andreitoma8/ERC721-Staking) by [andreitoma8](https://github.com/andreitoma8/ERC721-Staking)\r\n\r\nWe deploy the NFT Staking Smart contract using [thirdweb deploy](https://portal.thirdweb.com/thirdweb-deploy) and interact with all three of the contracts using the thirdweb [TypeScript](https://portal.thirdweb.com/typescript) and [React](https://portal.thirdweb.com/react) SDKs.\r\n\r\n**Check out the Demo here**: https://nft-staking-contract.thirdweb-example.com/\r\n\r\n## Tools\r\n\r\n- [**thirdweb Deploy**](https://portal.thirdweb.com/thirdweb-deploy): Deploy our `StakingContract.sol` smart contract with zero configuration by running `npx thirdweb deploy`.\r\n- [**thirdweb React SDK**](https://docs.thirdweb.com/react): to enable users to connect and disconnect their wallets with our website, and interact with our smart contracts using hooks like [useNFTDrop](https://portal.thirdweb.com/react/react.usenftdrop), [useToken](https://portal.thirdweb.com/react/react.usetoken), and [useContract](https://portal.thirdweb.com/react/react.usecontract).\r\n\r\n## Using This Repo\r\n\r\n- Create a copy of this repo by running the below command:\r\n\r\n```bash\r\nnpx thirdweb create --template nft-staking-app\r\n```\r\n\r\n- Deploy the `StakingContract.sol` smart contract by running the below command from the root of the project directory:\r\n\r\n```bash\r\nnpx thirdweb deploy\r\n```\r\n\r\n- Configure the network you deployed in [`index.js`](./src/index.js):\r\n\r\n```jsx\r\n// This is the chainId your dApp will work on.\r\nconst activeChainId = ChainId.Mumbai;\r\n```\r\n\r\n- Run the project locally:\r\n\r\n```bash\r\nnpm run dev\r\n```\r\n\r\n# Guide\r\n\r\nIn this section, we'll dive into the code and explain how it works.\r\n\r\n## NFT Staking Smart Contract\r\n\r\nThe NFT Staking contract in [StakingContract.sol](./StakingContract.sol) can be broken down into three parts:\r\n\r\n1. Staking\r\n2. Withdrawing\r\n3. Rewards\r\n\r\n### Staking\r\n\r\nNFTs can be staked by users to earn rewards, and are held by the smart contract until the user withdraws them.\r\n\r\nWe have two mappings to track which tokens are staked by which addresses and information about those addresses:\r\n\r\n```solidity\r\n    // Mapping of User Address to Staker info\r\n    mapping(address =\u003e Staker) public stakers;\r\n\r\n    // Mapping of Token Id to staker. Made for the SC to remeber\r\n    // who to send back the ERC721 Token to.\r\n    mapping(uint256 =\u003e address) public stakerAddress;\r\n```\r\n\r\nWhen the user calls the `stake` function on the smart contract, they smart contract transfers the NFT from their wallet to the contract:\r\n\r\n```solidity\r\n        // Transfer the token from the wallet to the Smart contract\r\n        nftCollection.transferFrom(msg.sender, address(this), _tokenId);\r\n```\r\n\r\nThe contract keeps track of the token's staked status and which address staked this token:\r\n\r\n```solidity\r\n        // Create StakedToken\r\n        StakedToken memory stakedToken = StakedToken(msg.sender, _tokenId);\r\n\r\n        // Add the token to the stakedTokens array\r\n        stakers[msg.sender].stakedTokens.push(stakedToken);\r\n\r\n        // Increment the amount staked for this wallet\r\n        stakers[msg.sender].amountStaked++;\r\n\r\n        // Update the mapping of the tokenId to the staker's address\r\n        stakerAddress[_tokenId] = msg.sender;\r\n\r\n        // Update the timeOfLastUpdate for the staker\r\n        stakers[msg.sender].timeOfLastUpdate = block.timestamp;\r\n```\r\n\r\nWe will talk about how the rewards system works and why we keep track of the `timeOfLastUpdate` and `amountStaked` in the **Rewards** section.\r\n\r\n### Withdrawing\r\n\r\nWithdrawing is essentially the opposite of staking.\r\n\r\nWe `transfer` the token back to the wallet address that staked it (that we store in the mapping).\r\n\r\n```solidity\r\n        // Wallet must own the token they are trying to withdraw\r\n        require(stakerAddress[_tokenId] == msg.sender, \"You don't own this token!\");\r\n\r\n        // Transfer the token back to the withdrawer\r\n        nftCollection.transferFrom(address(this), msg.sender, _tokenId);\r\n```\r\n\r\nWhen the user withdraws the token, we mark the `.staker` of this token inside the user's `stakedTokens` array to be `address(0)` in order to keep track of which tokens are no longer staked, without having to remove anything from the array:\r\n\r\n```solidity\r\n\r\n        // Find the index of this token id in the stakedTokens array\r\n        uint256 index = 0;\r\n        for (uint256 i = 0; i \u003c stakers[msg.sender].stakedTokens.length; i++) {\r\n            if (\r\n                stakers[msg.sender].stakedTokens[i].tokenId == _tokenId\r\n                \u0026\u0026\r\n                stakers[msg.sender].stakedTokens[i].staker != address(0)\r\n            ) {\r\n                index = i;\r\n                break;\r\n            }\r\n        }\r\n\r\n        // \"Remove\" this token from the stakedTokens array\r\n        stakers[msg.sender].stakedTokens[index].staker = address(0);\r\n```\r\n\r\n### Rewards\r\n\r\nRewards are calculated based on\r\n\r\n- How many NFTs the wallet has staked\r\n- How much time has passed\r\n- the `rewardsPerHour` rate configured in the contract.\r\n\r\nIn order to keep track of user's rewards and how they fluctuate over time, each staker has an `unclaimedRewards` field and a `timeOfLastUpdate` field.\r\n\r\nEvery time the user's rewards rate would change (e.g. they stake or withdraw an NFT), the `timeOfLastUpdate` and the `unclaimedRewards` fields are both updated.\r\n\r\nFor example, if a user staked 1 NFT for 1 hour, they would earn:\r\n\r\n```\r\n1 * 100,000 = 100,000\r\n```\r\n\r\nThen, if they staked another NFT after this hour, we somehow need to know how much they earnt up to this point, because their new rewards rate will increase after this new stake.\r\n\r\nSo, then it becomes:\r\n\r\n```\r\n1 * 100,000 * 1\r\n\r\n+\r\n\r\n2 * 100,000 * hours between this stake call and time now\r\n```\r\n\r\nThis is how we keep track of the user's rewards despite fluctuating rewards rates as they stake and withdraw NFTs.\r\n\r\nThe calculate rewards function:\r\n\r\n```solidity\r\n    function calculateRewards(address _staker)\r\n        internal\r\n        view\r\n        returns (uint256 _rewards)\r\n    {\r\n        return (((\r\n            ((block.timestamp - stakers[_staker].timeOfLastUpdate) *\r\n                stakers[_staker].amountStaked)\r\n        ) * rewardsPerHour) / 3600);\r\n    }\r\n```\r\n\r\nCalculate the total rewards owed to a user at the current point in time:\r\n\r\n```solidity\r\n    function availableRewards(address _staker) public view returns (uint256) {\r\n        uint256 rewards = calculateRewards(_staker) +\r\n            stakers[_staker].unclaimedRewards;\r\n        return rewards;\r\n    }\r\n```\r\n\r\nUpdate the information when the user `stake`s or `withdraw`s:\r\n\r\n```solidity\r\n        // Update the rewards for this user\r\n        uint256 rewards = calculateRewards(msg.sender);\r\n        stakers[msg.sender].unclaimedRewards += rewards;\r\n```\r\n\r\nPayout the user's rewards:\r\n\r\n```solidity\r\n    function claimRewards() external {\r\n        uint256 rewards = calculateRewards(msg.sender) +\r\n            stakers[msg.sender].unclaimedRewards;\r\n        require(rewards \u003e 0, \"You have no rewards to claim\");\r\n        stakers[msg.sender].timeOfLastUpdate = block.timestamp;\r\n        stakers[msg.sender].unclaimedRewards = 0;\r\n        rewardsToken.safeTransfer(msg.sender, rewards);\r\n    }\r\n```\r\n\r\n## Deploying the smart contract\r\n\r\nWe use [thirdweb deploy](https://portal.thirdweb.com/thirdweb-deploy) to deploy the Staking smart contract by running:\r\n\r\n```bash\r\nnpx thirdweb deploy\r\n```\r\n\r\nThis provides us with a link to deploy the contract via the [thirdweb dashboard](https://thirdweb.com/dashboard)\r\n\r\n## Front-end Application\r\n\r\nOn the front-end, we connect to all three of our smart contracts and interact with them using thirdweb's SDKs.\r\n\r\n### Configuring the ThirdwebProvider\r\n\r\nWe wrap our application in the `ThirdwebProvider` component to access all of the React SDK's hooks and configure the network we want to support.\r\n\r\n```jsx\r\n// This is the chainId your dApp will work on.\r\nconst activeChainId = ChainId.Mumbai;\r\n\r\nfunction MyApp({ Component, pageProps }: AppProps) {\r\n  return (\r\n    \u003cThirdwebProvider desiredChainId={activeChainId}\u003e\r\n      \u003cComponent {...pageProps} /\u003e\r\n    \u003c/ThirdwebProvider\u003e\r\n  );\r\n}\r\n```\r\n\r\n### Mint Page\r\n\r\nOn the [mint.tsx](./pages/mint.tsx), we connect to our NFT Drop contract using the [useNFTDrop](https://portal.thirdweb.com/react/react.usenftdrop) hook.\r\n\r\n```jsx\r\n// Get the NFT Collection contract\r\nconst nftDropContract = useNFTDrop(\r\n  \"0x322067594DBCE69A9a9711BC393440aA5e3Aaca1\" // your contract here\r\n);\r\n```\r\n\r\nAnd allow user's to mint an NFT from our contract using the `claim` method:\r\n\r\n```jsx\r\nconst tx = await nftDropContract?.claim(1); // 1 is quantity here\r\n```\r\n\r\n### Stake Page\r\n\r\nThe staking page connects to all three of our contracts:\r\n\r\n1. NFTDrop contract using [useNFTDrop](https://portal.thirdweb.com/react/react.usenftdrop)\r\n\r\n```jsx\r\nconst nftDropContract = useNFTDrop(nftDropContractAddress);\r\n```\r\n\r\n2. Token contract using [useToken](https://portal.thirdweb.com/react/react.usetoken)\r\n\r\n```jsx\r\nconst tokenContract = useToken(tokenContractAddress);\r\n```\r\n\r\n3. Staking contract using [useContract](https://portal.thirdweb.com/react/react.usecontract)\r\n\r\n```jsx\r\nconst { contract, isLoading } = useContract(stakingContractAddress);\r\n```\r\n\r\n**Loading Staked NFTs:**\r\n\r\n```jsx\r\nasync function loadStakedNfts() {\r\n  const stakedTokens = await contract?.call(\"getStakedTokens\", address);\r\n\r\n  // For each staked token, fetch it from the sdk\r\n  const stakedNfts = await Promise.all(\r\n    stakedTokens?.map(\r\n      async (stakedToken: { staker: string, tokenId: BigNumber }) =\u003e {\r\n        // Fetch metadata for each staked token id\r\n        const nft = await nftDropContract?.get(stakedToken.tokenId);\r\n        return nft;\r\n      }\r\n    )\r\n  );\r\n\r\n  // Store the result in state, now we have an array of NFT metadata.\r\n  setStakedNfts(stakedNfts);\r\n}\r\n```\r\n\r\n**Loading claimable rewards**:\r\n\r\n```jsx\r\nasync function loadClaimableRewards() {\r\n  const cr = await contract?.call(\"availableRewards\", address);\r\n}\r\n```\r\n\r\n**Staking NFTs**:\r\n\r\nIn order for the smart contract to have permission to transfer NFTs from our wallet, we need to ensure it has the required `approval`, which we do by calling the `setApprovalForAll` method for our NFTs in the NFT Drop contract.\r\n\r\n```jsx\r\nasync function stakeNft(id: BigNumber) {\r\n  if (!address) return;\r\n\r\n  const isApproved = await nftDropContract?.isApproved(\r\n    address,\r\n    stakingContractAddress\r\n  );\r\n  // If not approved, request approval\r\n  if (!isApproved) {\r\n    await nftDropContract?.setApprovalForAll(stakingContractAddress, true);\r\n  }\r\n  const stake = await contract?.call(\"stake\", id);\r\n}\r\n```\r\n\r\n**Withdrawing NFTs**:\r\n\r\n```jsx\r\nasync function withdraw(id: BigNumber) {\r\n  const withdraw = await contract?.call(\"withdraw\", id);\r\n}\r\n```\r\n\r\n**Claiming Rewards**:\r\n\r\n```jsx\r\nasync function claimRewards() {\r\n  const claim = await contract?.call(\"claimRewards\");\r\n}\r\n```\r\n\r\n## Join our Discord!\r\n\r\nFor any questions, suggestions, join our discord at [https://discord.gg/thirdweb](https://discord.gg/thirdweb).\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslycompiler%2Fnft-staking-solidity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslycompiler%2Fnft-staking-solidity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslycompiler%2Fnft-staking-solidity/lists"}