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
509 changes: 509 additions & 0 deletions packages/hardhat/contracts/MiniRaffleV3.sol

Large diffs are not rendered by default.

673 changes: 673 additions & 0 deletions packages/hardhat/contracts/MiniRaffleV4.sol

Large diffs are not rendered by default.

150 changes: 150 additions & 0 deletions packages/hardhat/contracts/physicalNFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";

/// @notice Minimal interface your Raffle uses (keep for compatibility).
interface IPrizeNFT {
function mintTo(address to) external returns (uint256);
}

/// @title PhysicalPrizeNFT
/// @dev Voucher-style ERC721 used to redeem a physical prize.
/// - Raffle contract mints to the winner
/// - Winner must `claim()` before `expiry`
/// - If not claimed by expiry, owner can `revokeExpired()` and re-raffle
contract PhysicalPrizeNFT is ERC721URIStorage, Ownable {
// ---- Roles ----
address public raffle; // AkibaRaffle proxy address allowed to mint

// ---- Config ----
uint256 public defaultClaimWindow = 30 days; // can be changed by owner

// ---- Prize state ----
struct Prize {
uint64 expiry; // unix timestamp when claim window ends
bool claimed; // winner marked as claimed
uint64 roundId; // optional: raffle round for UX/traceability
}

// tokenId => prize info
mapping(uint256 => Prize) public prizeInfo;

// simple incremental token id
uint256 private _nextId = 1;

// ---- Events ----
event RaffleSet(address indexed raffle);
event DefaultClaimWindowSet(uint256 seconds_);
event PrizeMinted(uint256 indexed tokenId, address indexed to, uint64 roundId, uint64 expiryTs, string tokenURI_);
event PrizeClaimed(uint256 indexed tokenId, address indexed claimer);
event PrizeRevoked(uint256 indexed tokenId);

// ---- Modifiers ----
modifier onlyRaffle() {
require(msg.sender == raffle, "PrizeNFT: not raffle");
_;
}

constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}

// -------- Admin --------

function setRaffle(address raffle_) external onlyOwner {
require(raffle_ != address(0), "PrizeNFT: zero raffle");
raffle = raffle_;
emit RaffleSet(raffle_);
}

/// @notice Set default claim window (in seconds). E.g., 30 days = 30 * 86400
function setDefaultClaimWindow(uint256 seconds_) external onlyOwner {
require(seconds_ > 0, "PrizeNFT: zero window");
defaultClaimWindow = seconds_;
emit DefaultClaimWindowSet(seconds_);
}

// -------- Minting (Raffle-only) --------

/// @notice Backwards-compatible mint with default window and empty URI.
function mintTo(address to) external onlyRaffle returns (uint256 tokenId) {
tokenId = _mintPrize(to, 0, 0, "");
}

/// @notice Preferred mint with per-round metadata and optional override window/URI.
/// @param to Winner address
/// @param roundId Optional: AkibaRaffle round id (0 if unused)
/// @param claimWindowSeconds 0 => use defaultClaimWindow; otherwise custom (e.g., 10 days)
/// @param tokenURI_ Optional metadata URI (empty string to skip)
function mintTo(
address to,
uint64 roundId,
uint256 claimWindowSeconds,
string memory tokenURI_
) external onlyRaffle returns (uint256 tokenId) {
tokenId = _mintPrize(to, roundId, claimWindowSeconds, tokenURI_);
}

function _mintPrize(
address to,
uint64 roundId,
uint256 claimWindowSeconds,
string memory tokenURI_
) internal returns (uint256 tokenId) {
require(to != address(0), "PrizeNFT: zero to");

uint256 window = (claimWindowSeconds == 0) ? defaultClaimWindow : claimWindowSeconds;
require(window > 0, "PrizeNFT: bad window");

tokenId = _nextId++;
_safeMint(to, tokenId);

if (bytes(tokenURI_).length != 0) {
_setTokenURI(tokenId, tokenURI_);
}

uint64 expiryTs = uint64(block.timestamp + window);
prizeInfo[tokenId] = Prize({
expiry: expiryTs,
claimed: false,
roundId: roundId
});

emit PrizeMinted(tokenId, to, roundId, expiryTs, tokenURI_);
}

// -------- Claim / Revoke --------

/// @notice Winner claims the physical prize before expiry (on-chain acknowledgement).
function claim(uint256 tokenId) external {
require(ownerOf(tokenId) == msg.sender, "PrizeNFT: not owner");
Prize storage p = prizeInfo[tokenId];
require(!p.claimed, "PrizeNFT: already claimed");
require(block.timestamp <= p.expiry, "PrizeNFT: expired");

p.claimed = true;
emit PrizeClaimed(tokenId, msg.sender);
}

/// @notice After expiry and if unclaimed, owner can revoke (burn) to re-raffle.
function revokeExpired(uint256 tokenId) external onlyOwner {
Prize storage p = prizeInfo[tokenId];
require(!p.claimed, "PrizeNFT: already claimed");
require(block.timestamp > p.expiry, "PrizeNFT: not expired");

_burn(tokenId);
delete prizeInfo[tokenId];
emit PrizeRevoked(tokenId);
}

// -------- Views --------

function getPrize(uint256 tokenId)
external
view
returns (uint64 expiry, bool claimed, uint64 roundId)
{
Prize memory p = prizeInfo[tokenId];
return (p.expiry, p.claimed, p.roundId);
}
}
30 changes: 18 additions & 12 deletions packages/hardhat/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,41 @@ const config: HardhatUserConfig = {

},
etherscan: {
apiKey: {
alfajores: process.env.CELOSCAN_API_KEY ?? '',
celo: process.env.CELOSCAN_API_KEY ?? '',
sepolia: 'GG98NBUZX46RSPK26MDUA76KSVWIHHDPHI'
},
apiKey: process.env.CELOSCAN_API_KEY ?? '',

customChains: [
{
chainId: 44_787,
network: 'alfajores',
chainId: 44787,
urls: {
apiURL: 'https://api-alfajores.celoscan.io/api',
apiURL: 'https://api-alfajores.celoscan.io/api', // CeloScan supports Etherscan-compatible API
browserURL: 'https://alfajores.celoscan.io',
},
},
{
chainId: 42_220,
network: 'celo',
chainId: 42220,
urls: {
apiURL: 'https://api.celoscan.io/api',
browserURL: 'https://celoscan.io/',
browserURL: 'https://celoscan.io',
},
},

],
},
},
sourcify: {
enabled: false,
},
solidity: '0.8.24',
solidity:{
version: '0.8.24',
settings: {
// These three are the big wins for code size
optimizer: { enabled: true, runs: 200 }, // try 200, 1, or 800 depending on size vs. runtime gas
viaIR: true, // IR pipeline shrinks bytecode a lot
metadata: { bytecodeHash: "none" }, // smaller deploy bytecode (runtime size is the EIP-170 limit)
// evmVersion: "paris", // uncomment if you want to pin EVM version
},
}

};

export default config;
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,11 @@
"MiniRaffles#MiniRaffle": "0xA1F1Cd3b90f49c9d44ed324C69869df139616d55",
"MiniRafflesForNiko#MiniRaffle": "0x8762277745b6f05ceb3F2c2E85FF3062A57482B6",
"MiniRaffleModule#MiniRaffle": "0xA678CaCD19485f37ebe886a5fbE2cE94E0994497",
"MiniRaffleModule#ERC1967Proxy": "0x917124eea88426c9D66c21bA155b925E8F38f8aA"
"MiniRaffleModule#ERC1967Proxy": "0x917124eea88426c9D66c21bA155b925E8F38f8aA",
"Physical#PhysicalPrizeNFT": "0x8F60907f41593d4B41f5e0cEa48415cd61854a79",
"points#AkibaMiles": "0xd59AE111d976342ff58c6dE2B6f2b002415825C1",
"AMilesV3#akiba_impl": "0x10EA378Ea0429c27A892d9349C0cC5fC373bd9cb",
"AMilesV3#akiba_proxy": "0x72fEFD4e943475c5cB7Cf11753fE60d04aEb7ff0",
"AkibaRaffleV3_Upgrade#AkibaRaffleV3": "0x72fEFD4e943475c5cB7Cf11753fE60d04aEb7ff0",
"AkibaRaffleV3_Upgrade#akiba_impl_vNext": "0xb533CB94DeaBDd37F870E47510a03539C413CEFF"
}
43 changes: 42 additions & 1 deletion packages/hardhat/ignition/deployments/chain-44787/journal.jsonl

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/hardhat/ignition/modules/MiniPay.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

const RaffleModule = buildModule("vaultToken", (m) => {
const RaffleModule = buildModule("Physical", (m) => {
// Initial numbers array to deploy the contract with
const initialNumbers = [1, 2, 3, 4, 5]; // <-- you can change these

const raffle = m.contract("akUSDT")
const raffle = m.contract("PhysicalPrizeNFT", ["Akiba Prize", "AKIBA"])

return { raffle };
});
Expand Down
4 changes: 2 additions & 2 deletions packages/hardhat/ignition/modules/MiniRaffle.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

const RaffleModule = buildModule("MiniRafflesForNiko", (m) => {
const RaffleModule = buildModule("points", (m) => {
// Initial numbers array to deploy the contract with
const initialNumbers = [1, 2, 3, 4, 5]; // <-- you can change these
const cUSD = "0x765de816845861e75a25fca122bb6898b8b1282a";
const usdt = '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e'
const MiniPoints = '0xb0012Ff26b6eB4F75d09028233204635c0332050'
const raffle = m.contract("MiniRaffle",[MiniPoints, cUSD, usdt])
const raffle = m.contract("AkibaMiles")

return { raffle };
});
Expand Down
17 changes: 9 additions & 8 deletions packages/hardhat/ignition/modules/proxyDeployer.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
import { ethers } from "ethers";

const MiniRaffleModule = buildModule("MilesVault", (m) => {
const MiniRaffleModule = buildModule("AMilesV3", (m) => {
/*************
* CONFIG *
*************/
// <<< fill in real addresses before running >>>


const aCelUSDT = "0xDeE98402A302e4D707fB9bf2bac66fAEEc31e8Df";
const minipoints = "0xd59AE111d976342ff58c6dE2B6f2b002415825C1";
const usdt = '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e'
const vaultToken = '0x9eF834341C0aaE253206e838c37518d1E1927716'
const pool = '0x3E59A31363E2ad014dcbc521c4a0d5757d9f3402'
const safe = '0x9E621004591Fa0224182083D535ACBee90914e63'
const cUSD = '0x874069fa1eb16d44d622f2e0ca25eea172369bc1'
const owner = '0x03909bb1E9799336d4a8c49B74343C2a85fDad9d'
const prize = '0x8F60907f41593d4B41f5e0cEa48415cd61854a79'
const referralCode = 0;


/*************
* 1. Deploy implementation
*************/
const implementation = m.contract("AkibaMilesVaultUUPS", [], { id: "akiba_impl" });
const implementation = m.contract("AkibaRaffleV3", [], { id: "akiba_impl" });

/*************
* 2. Encode initializer data
*************/
const iface = new ethers.utils.Interface([
"function initialize(address,address,address,address, address, uint16)"
"function initialize(address,address,address,address,address)"
]);
const initData = iface.encodeFunctionData("initialize", [
usdt, aCelUSDT, pool,vaultToken,safe,referralCode
minipoints, cUSD, usdt,prize,owner
]);

/*************
Expand Down
25 changes: 25 additions & 0 deletions packages/hardhat/ignition/modules/upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

/**
* Upgrades the existing UUPS proxy at PROXY_ADDR to a new AkibaRaffleV3 implementation.
* Make sure the deployer account is the contract owner (authorized to upgrade).
*/
const PROXY_ADDR = "0xd75dfa972c6136f1c594fec1945302f885e1ab29";

const AkibaRaffleV3_Upgrade = buildModule("AkibaRaffleV4_Upgrade", (m) => {
// 1) Deploy new implementation
const newImpl = m.contract("AkibaRaffleV4", [], { id: "akiba_impl_v4" });

// 2) Treat the existing proxy as AkibaRaffleV3
const proxyAsAkiba = m.contractAt("AkibaRaffleV3", PROXY_ADDR);

// 3) Call upgradeTo(newImpl) via the proxy
m.call(proxyAsAkiba, "upgradeTo", [newImpl], {
id: "akiba_upgrade_call",
after: [newImpl],
});

return { newImpl, proxyAsAkiba };
});

export default AkibaRaffleV3_Upgrade;
2 changes: 1 addition & 1 deletion packages/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"@nomicfoundation/hardhat-ignition-ethers": "^0.15.0",
"@nomicfoundation/hardhat-network-helpers": "^1.0.11",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.11",
"@nomicfoundation/hardhat-verify": "^2.1.3",
"@nomicfoundation/ignition-core": "^0.15.5",
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@openzeppelin/contracts": "^4.8.3",
Expand Down
Loading