Created
May 25, 2026 14:12
-
-
Save procorbin/4c0b2f6966b7b61194367321d361fad4 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.34+commit.80d5c536.js&optimize=undefined&runs=200&gist=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // SPDX-License-Identifier: MIT | |
| pragma solidity ^0.8.20; | |
| import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
| import "@openzeppelin/contracts/access/Ownable.sol"; | |
| import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | |
| import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; | |
| /** | |
| * ShibaBlastarDistributor | |
| * | |
| * Allows users to claim ShibaBlastar tokens using a server-signed voucher. | |
| * The server (Symfony) signs the claim off-chain; the user broadcasts the | |
| * transaction and pays gas fees themselves. | |
| */ | |
| contract ShibaBlastarDistributor is Ownable { | |
| IERC20 public immutable token; | |
| // Address that holds the ShibaBlastar tokens and has approved this contract | |
| address public tokenHolder; | |
| // Nonces used to prevent replay attacks | |
| mapping(uint256 => bool) private _usedNonces; | |
| event TokensClaimed(address indexed recipient, uint256 amount, uint256 nonce); | |
| event TokenHolderUpdated(address indexed previousHolder, address indexed newHolder); | |
| constructor(address tokenAddress, address _tokenHolder) Ownable(msg.sender) { | |
| token = IERC20(tokenAddress); | |
| tokenHolder = _tokenHolder; | |
| } | |
| /** | |
| * Claim tokens using a server-signed voucher. | |
| * | |
| * The server signs off-chain: | |
| * keccak256(abi.encodePacked(recipientAddress, amount, nonce)) | |
| * | |
| * @param amount Amount of tokens to claim (in wei, 18 decimals) | |
| * @param nonce Unique identifier for this claim (from server) | |
| * @param signature ECDSA signature produced by the server (owner) | |
| */ | |
| function claimTokens(uint256 amount, uint256 nonce, bytes memory signature) external { | |
| require(!_usedNonces[nonce], "Nonce already used"); | |
| require(amount > 0, "Amount must be greater than zero"); | |
| bytes32 message = keccak256(abi.encodePacked(msg.sender, amount, nonce)); | |
| bytes32 ethSignedMessage = MessageHashUtils.toEthSignedMessageHash(message); | |
| address signer = ECDSA.recover(ethSignedMessage, signature); | |
| require(signer == owner(), "Invalid signature"); | |
| _usedNonces[nonce] = true; | |
| require( | |
| token.transferFrom(tokenHolder, msg.sender, amount), | |
| "Token transfer failed" | |
| ); | |
| emit TokensClaimed(msg.sender, amount, nonce); | |
| } | |
| /** | |
| * Check if a nonce has already been used. | |
| */ | |
| function isNonceUsed(uint256 nonce) external view returns (bool) { | |
| return _usedNonces[nonce]; | |
| } | |
| /** | |
| * Update the token holder address (onlyOwner). | |
| */ | |
| function setTokenHolder(address _tokenHolder) external onlyOwner { | |
| emit TokenHolderUpdated(tokenHolder, _tokenHolder); | |
| tokenHolder = _tokenHolder; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment