Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- ZKVerifier
- Optimization enabled
- false
- Compiler version
- v0.8.24+commit.e11b9ed9
- EVM Version
- Verified at
- 2026-10-11T10:45:03.672193Z
Constructor Arguments
000000000000000000000000f119beb1b343e46c4ed0e78362cd3f87e87ef4cc
Arg [0] (address) : 0xf119beb1b343e46c4ed0e78362cd3f87e87ef4cc
contracts/ZKVerifier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./AdminZamik.sol";
interface IRWAToken {
function mintWithProof(address to, uint256 amount, bytes32 proofHash) external;
function mintFee(address to, uint256 amount) external;
function assetValueUSD() external view returns (uint256);
function feeRecipient() external view returns (address);
}
/**
* @title ZKVerifier
* @notice Verifies Groth16 ZK proofs for RWA ownership claims
*
* Security fixes (v2):
* - recipient mora biti msg.sender → prepreči frontrunning
* - amount je omejen na maxMintable (izračunan iz assetValueUSD) → prepreči neomejeni mint
* - amount > 0 check
* - proofHash vključuje recipient in amount
* - registeredCommitments počiščen po mintu
* - setRwaToken odstranjen (mrtva koda — konstruktor že nastavi token)
*/
contract ZKVerifier is Ownable, AdminZamik {
// === snarkjs-generated VK constants (from rwa_proof_verifier.sol) ===
// Scalar field size
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Base field size
uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
// Verification Key data
uint256 constant alphax = 20491192805390485299153009773594534940189261866228447918068658471970481763042;
uint256 constant alphay = 9383485363053290200918347156157836566562967994039712273449902621266178545958;
uint256 constant betax1 = 4252822878758300859123897981450591353533073413197771768651442665752259397132;
uint256 constant betax2 = 6375614351688725206403948262868962793625744043794305715222011528459656738731;
uint256 constant betay1 = 21847035105528745403288232691147584728191162732299865338377159692350059136679;
uint256 constant betay2 = 10505242626370262277552901082094356697409835680220590971873171140371331206856;
uint256 constant gammax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
uint256 constant deltax1 = 7634694382555350249182314939945794752801670761871442236918755611747247981945;
uint256 constant deltax2 = 7785580959050401778007424472707980943542624615417776150098749352587788464225;
uint256 constant deltay1 = 19797755760015837479531733607381070868376789853134730363187779619202029563244;
uint256 constant deltay2 = 19885317433472164527823093690594480541727870532036884219862146880419864556528;
uint256 constant IC0x = 5313285605904455468883044812252347068829101215429313644709835484872601966339;
uint256 constant IC0y = 2611852344649716617799171908939889777235094010478351269767823832353876722738;
uint256 constant IC1x = 3721507159761245641438686913490987492444926420712387384343195655500770466953;
uint256 constant IC1y = 10884128966892936170203469452538504289719178213118369098919656563615016733370;
uint256 constant IC2x = 11919587884350751358344086380913156139705038053372491645096979466761922566199;
uint256 constant IC2y = 11737085826314024049430266969005569805271747491015946543900350977318458074708;
// Memory data
uint16 constant pVk = 0;
uint16 constant pPairing = 128;
uint16 constant pLastMem = 896;
// === end generated constants ===
address public entropyOracle;
/**
* VEC SREDSTEV, VSAKO SVOJ ZETON
*
* En ZKVerifier strezhe vsem zetonom; vsak zeton predstavlja eno sredstvo.
* Commitment se registrira za DOLOCEN zeton, zato dokaz, izdan za sredstvo A,
* ne more kovati na sredstvu B, tudi ce je sam po sebi veljaven.
*
* Vez med commitmentom in zetonom je stanje na verigi, ne javni signal
* dokaza; ZK vezje je zato neodvisno od stevila sredstev.
*/
mapping(address => bool) public pooblasceniZetoni; // kateri zetoni smejo kovati prek tega verifierja
address public tovarna; // RWATokenFactory — sme registrirati svoje zetone
// Asset registry: samo vnaprej registrirani assetCommitment-i lahko mintajo.
// assetCommitment = Poseidon(privateKey, entropyNonce) = javni signal _pubSignals[0]
mapping(uint256 => address) public commitmentZeton; // commitment → za KATERI zeton je registriran
mapping(uint256 => bool) public commitmentUsed; // porabljeno (vsak asset minta enkrat)
/// @notice Ali je commitment registriran (za katerikoli zeton).
function registeredCommitments(uint256 commitment) external view returns (bool) {
return commitmentZeton[commitment] != address(0);
}
// Pooblaščeni UORS operaterji — smejo registrirati commitmente
mapping(address => bool) public authorizedUORS;
// Tokenizacijska provizija: 0,5% ob mintu
uint256 public constant MINT_FEE_BPS = 50; // 50 = 0,5%
event ProofVerified(address indexed claimant, bytes32 proofHash);
event TovarnaNastavljena(address indexed tovarna);
event ZetonPooblascen(address indexed zeton);
event ZetonOdvzet(address indexed zeton);
event MintFeeCollected(address indexed feeRecipient, uint256 fee);
event AssetRegistered(uint256 indexed commitment, address indexed zeton);
event AssetUnregistered(uint256 indexed commitment);
event AssetMinted(uint256 indexed commitment, address indexed recipient, uint256 amount, address indexed zeton);
event UORSAuthorized(address indexed uors);
event UORSRevoked(address indexed uors);
event Zaustavljeno(address indexed kdo);
event Sproscen(address indexed kdo);
// ─────────────────────────────────────────────────────────────────────────
// SISTEMSKA ZAUSTAVITEV
//
// Zaustavitev velja za VSE kovanje hkrati in ne presoja posameznega sredstva;
// zamrznitev posameznega zetona je v pristojnosti pooblascenega UORS
// operaterja (glej RWAToken.zamrzni).
//
// `pause` sme sprozit vec naslovov (lastnik + pooblasceni zaustavljalci,
// npr. nadzorni proces), `unpause` pa samo lastnik: ustavitev mora biti
// hitra, ponoven zagon pa premisljen.
// ─────────────────────────────────────────────────────────────────────────
bool public paused;
mapping(address => bool) public jeZaustavljalec;
event ZaustavljalecSpremenjen(address indexed kdo, bool lahko);
/// @dev Takojsno v obe smeri: ob razkritem kljucu odvzema ne smes cakati.
function nastaviZaustavljalca(address kdo, bool lahko) external onlyOwner {
require(kdo != address(0), "Zero address");
jeZaustavljalec[kdo] = lahko;
emit ZaustavljalecSpremenjen(kdo, lahko);
}
function pause() external {
require(msg.sender == owner() || jeZaustavljalec[msg.sender], "Ni pooblascen za zaustavitev");
paused = true;
emit Zaustavljeno(msg.sender);
}
function unpause() external onlyOwner {
paused = false;
emit Sproscen(msg.sender);
}
/// @dev Registracija commitmenta je izjava overitelja (UORS), zato je skrbnik
/// (owner) NE more opraviti sam: skrbnik lahko rece samo »ne« (revokeUORS,
/// unregisterCommitment, pause), nikoli »da«.
modifier onlyUORS() {
require(authorizedUORS[msg.sender], "Not authorized");
_;
}
/// @dev Krozne odvisnosti ni vec: verifier se postavi PRVI, zetoni pridejo za njim.
constructor(address _entropyOracle) Ownable(msg.sender) {
entropyOracle = _entropyOracle;
}
// ─────────────────────────────────────────────────────────────────────────
// Pooblascanje zetonov
//
// Kdor sme dodati zeton, sme kovati nova sredstva — to je enako mocno kot
// pooblastitev UORS operaterja. Zato gre owner-jeva pot SKOZI ZAMIK.
//
// Tovarna je izjema in mora biti: ce bi vsako novo sredstvo cakalo 48 ur,
// bi bilo ustvarjanje sredstev neuporabno. Namesto tega gre skozi zamik
// NASTAVITEV TOVARNE — enkrat, vidno, vnaprej — potem pa tovarna registrira
// samo zetone, ki jih je sama postavila.
// ─────────────────────────────────────────────────────────────────────────
function napoviNastaviTovarno(address _tovarna) external onlyOwner {
require(_tovarna != address(0), "Zero address");
_napovej(keccak256(abi.encode("nastaviTovarno", _tovarna)), "ZKVerifier.nastaviTovarno");
}
function nastaviTovarno(address _tovarna) external onlyOwner {
require(_tovarna != address(0), "Zero address");
_porabi(keccak256(abi.encode("nastaviTovarno", _tovarna)), "ZKVerifier.nastaviTovarno");
tovarna = _tovarna;
emit TovarnaNastavljena(_tovarna);
}
/// @notice Tovarna registrira zeton, ki ga je pravkar postavila.
function registrirajZeton(address zeton) external {
require(msg.sender == tovarna, "Samo tovarna");
require(zeton != address(0), "Zero address");
pooblasceniZetoni[zeton] = true;
emit ZetonPooblascen(zeton);
}
function napoviDodajZeton(address zeton) external onlyOwner {
require(zeton != address(0), "Zero address");
_napovej(keccak256(abi.encode("dodajZeton", zeton)), "ZKVerifier.dodajZeton");
}
/// @notice Rocno pooblastilo zetona (mimo tovarne) — pod zamikom.
function dodajZeton(address zeton) external onlyOwner {
require(zeton != address(0), "Zero address");
_porabi(keccak256(abi.encode("dodajZeton", zeton)), "ZKVerifier.dodajZeton");
pooblasceniZetoni[zeton] = true;
emit ZetonPooblascen(zeton);
}
/// @notice Odvzem je TAKOJSEN — isto merilo kot revokeUORS: nevarnost ne caka.
/// @dev Samo za zeton, ki je res pooblascen: dogodek ZetonOdvzet bereta
/// admin dnevnik in watchdog, zato ne sme pokazati odvzema, ki ni nicesar odvzel.
function odvzemiZeton(address zeton) external onlyOwner {
require(pooblasceniZetoni[zeton], "Zeton ni pooblascen");
pooblasceniZetoni[zeton] = false;
emit ZetonOdvzet(zeton);
}
/**
* @notice Registrira legitimen assetCommitment — samo lastnik (izdajatelj sredstva).
* @dev Lastnik sredstva izračuna commitment = Poseidon(privateKey, entropyNonce)
* in deli SAMO commitment (ne ključa). Izdajatelj ga tu registrira.
*/
/**
* PODELITEV MOCI GRE SKOZI ZAMIK, ODVZEM NE.
*
* Nov UORS lahko registrira commitmente, ti pa so pot do kovanja zetonov.
* Podtaknjen operater je zato med najmocnejsimi napadi, ki jih owner kljuc
* sploh omogoca — in je hkrati napad, ki ga je mogoce VIDETI VNAPREJ.
* Zato javna napoved + zamik.
*
* revokeUORS ostaja takojsen: ob razkritem kljucu operaterja ga moras vreci
* ven zdaj, ne cez 48 ur. Nevarnost caka, zasilna zavora ne.
*/
function napoviAuthorizeUORS(address uors) external onlyOwner {
require(uors != address(0), "Zero address");
_napovej(keccak256(abi.encode("authorizeUORS", uors)), "ZKVerifier.authorizeUORS");
}
/// @notice Pooblasti UORS operaterja za registracijo commitmentov.
function authorizeUORS(address uors) external onlyOwner {
require(uors != address(0), "Zero address");
_porabi(keccak256(abi.encode("authorizeUORS", uors)), "ZKVerifier.authorizeUORS");
authorizedUORS[uors] = true;
emit UORSAuthorized(uors);
}
/// @notice Prekliče pooblastilo UORS operaterja.
function revokeUORS(address uors) external onlyOwner {
authorizedUORS[uors] = false;
emit UORSRevoked(uors);
}
/**
* @notice Registrira commitment ZA DOLOCEN zeton (sredstvo).
* @dev Prav ta vez preprecuje, da bi dokaz za sredstvo A kovav na sredstvu B.
* Brez nje bi vsak veljaven dokaz odprl vsak zeton.
*/
function registerCommitment(uint256 commitment, address zeton) external onlyUORS {
require(commitment != 0, "Zero commitment");
require(pooblasceniZetoni[zeton], "Zeton ni pooblascen");
require(commitmentZeton[commitment] == address(0), "Already registered");
commitmentZeton[commitment] = zeton;
emit AssetRegistered(commitment, zeton);
}
/// @notice Paketna registracija — vsi commitmenti za ISTO sredstvo.
/// @dev Vse ali nic: nicelni ali ze registriran commitment revertira cel paket,
/// enako kot posamicna pot. Polovicno opravljen paket ne sme biti videti
/// enako kot opravljen.
function registerCommitments(uint256[] calldata commitments, address zeton) external onlyUORS {
require(pooblasceniZetoni[zeton], "Zeton ni pooblascen");
for (uint256 i = 0; i < commitments.length; i++) {
uint256 c = commitments[i];
require(c != 0, "Zero commitment");
require(commitmentZeton[c] == address(0), "Already registered");
commitmentZeton[c] = zeton;
emit AssetRegistered(c, zeton);
}
}
/// @notice Prekliče registracijo (le če še ni bil porabljen).
function unregisterCommitment(uint256 commitment) external onlyOwner {
require(commitmentZeton[commitment] != address(0), "Not registered");
require(!commitmentUsed[commitment], "Already used");
commitmentZeton[commitment] = address(0);
emit AssetUnregistered(commitment);
}
/// @notice Onemogoceno: renounceOwnership() bi trajno onemogocil
/// (de)avtorizacijo UORS operatorjev in registracijo commitmentov.
function renounceOwnership() public pure override {
revert("Renounce onemogocen");
}
/**
* @notice Verify ZK proof and mint RWA tokens to caller.
* @dev recipient je vedno msg.sender — prepreči frontrunning.
* amount je omejen na maxMintable = assetValueUSD / 1e16
* (1 RWA token = $0.01 USD pri 18 decimalkah).
* Prilagodi delilnik glede na tvojo tokenomiko.
*/
function verifyAndMint(
uint[2] calldata _pA,
uint[2][2] calldata _pB,
uint[2] calldata _pC,
uint[2] calldata _pubSignals,
address zeton,
uint256 amount
) external {
// FIX #2: recipient je vedno msg.sender — prepreči frontrunning iz mempoola
address recipient = msg.sender;
// FIX #1a: amount > 0
require(amount > 0, "Zero amount");
require(!paused, "Kovanje je zaustavljeno");
require(pooblasceniZetoni[zeton], "Zeton ni pooblascen");
// Asset registry: dokaz mora pripadati registriranemu, še neporabljenemu sredstvu
uint256 commitment = _pubSignals[0];
// ⚠️ Jedro locitve sredstev: commitment mora biti registriran ZA TA zeton.
// Brez te vrstice bi veljaven dokaz za sredstvo A koval na sredstvu B.
require(commitmentZeton[commitment] == zeton, "Commitment ni za ta zeton");
require(!commitmentUsed[commitment], "Asset already minted");
// ─────────────────────────────────────────────────────────────────────
// Kolicina je vezana na DOKAZANO vrednost, ne le na prijavljeno.
//
// Veljati morata obe omejitvi:
// 1. dokazana vrednost (`_pubSignals[1]`) ne sme presegati vrednosti,
// ki jo je za sredstvo prijavil overitelj — ta je zgornja meja,
// 2. kovana kolicina ne sme presegati dokazane vrednosti.
//
// Sama preverba lastnistva torej ne zadosca za poljubno kolicino.
//
// Enote: dokazana vrednost je v celih enotah valute, `assetValueUSD` ima
// 18 decimalk (od tod mnozenje z 1e18); faktor 100 pomeni 1 zeton = 0,01.
// ─────────────────────────────────────────────────────────────────────
// Javni signal se tu se NI preverjen z dokazom (ta je drag in pride zadnji),
// zato je racunanje zapisano tako, da ne more prekoraciti obsega: za
// vsak vhod pride berljiv razlog, nikoli panic.
uint256 dokazanaVrednost = _pubSignals[1];
require(dokazanaVrednost < r, "Javni signal izven polja");
require(dokazanaVrednost > 0, "Dokazana vrednost je 0");
// d * 1e18 <= V <=> d <= V / 1e18 (celostevilsko enakovredno)
require(dokazanaVrednost <= IRWAToken(zeton).assetValueUSD() / 1e18,
"Dokazana vrednost presega prijavljeno");
// amount <= d * 1e20 <=> (amount - 1) / 1e20 < d (amount > 0)
require((amount - 1) / 1e20 < dokazanaVrednost,
"Kolicina presega dokazano vrednost");
require(verifyProof(_pA, _pB, _pC, _pubSignals), "Invalid ZK proof");
commitmentUsed[commitment] = true;
// FIX #3: počisti registracijo po mintu (clean state)
commitmentZeton[commitment] = address(0);
bytes32 proofHash = _odtisDokaza(_pA, _pB, _pC, _pubSignals, recipient, amount, zeton);
uint256 mintFee = amount * MINT_FEE_BPS / 10_000;
IRWAToken(zeton).mintWithProof(recipient, amount - mintFee, proofHash);
// Klic VEDNO, tudi ko se provizija zaokrozi na 0 (amount < 200) —
// sicer zastavica `provizijaOdprta` v zetonu ostane odprta.
address prejemnikProvizije = IRWAToken(zeton).feeRecipient();
IRWAToken(zeton).mintFee(prejemnikProvizije, mintFee);
emit MintFeeCollected(prejemnikProvizije, mintFee);
emit AssetMinted(commitment, recipient, amount - mintFee, zeton);
emit ProofVerified(recipient, proofHash);
}
/**
* @dev Locena funkcija zato, da `verifyAndMint` ne pride v "stack too deep" ob
* Groth16 assembly. Vsebinsko je pomembno TO, kaj je v odtisu:
*
* · recipient in amount — auditability (FIX #4)
* · zeton — sicer bi bil isti odtis veljaven na VEC sredstvih;
* to je razsiritev ugotovitve F8 (ponovitev ne le
* med verigami, ampak med sredstvi)
* · block.chainid — F8 sama: prepreci ponovitev na drugi verigi
*/
function _odtisDokaza(
uint[2] calldata _pA,
uint[2][2] calldata _pB,
uint[2] calldata _pC,
uint[2] calldata _pubSignals,
address recipient,
uint256 amount,
address zeton
) private view returns (bytes32) {
return keccak256(abi.encode(_pA, _pB, _pC, _pubSignals, recipient, amount, zeton, block.chainid));
}
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[2] calldata _pubSignals) internal view returns (bool result) {
assembly {
function checkField(v) {
if iszero(lt(v, r)) {
mstore(0, 0)
revert(0, 0)
}
}
// G1 function to multiply a G1 value(x,y) to value in an address
function g1_mulAccC(pR, x, y, s) {
let success
let mIn := mload(0x40)
mstore(mIn, x)
mstore(add(mIn, 32), y)
mstore(add(mIn, 64), s)
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
if iszero(success) {
mstore(0, 0)
revert(0, 0)
}
mstore(add(mIn, 64), mload(pR))
mstore(add(mIn, 96), mload(add(pR, 32)))
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
if iszero(success) {
mstore(0, 0)
revert(0, 0)
}
}
function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk {
let _pPairing := add(pMem, pPairing)
let _pVk := add(pMem, pVk)
mstore(_pVk, IC0x)
mstore(add(_pVk, 32), IC0y)
// Compute the linear combination vk_x
g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0)))
g1_mulAccC(_pVk, IC2x, IC2y, calldataload(add(pubSignals, 32)))
// -A
mstore(_pPairing, calldataload(pA))
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))
// B
mstore(add(_pPairing, 64), calldataload(pB))
mstore(add(_pPairing, 96), calldataload(add(pB, 32)))
mstore(add(_pPairing, 128), calldataload(add(pB, 64)))
mstore(add(_pPairing, 160), calldataload(add(pB, 96)))
// alpha1
mstore(add(_pPairing, 192), alphax)
mstore(add(_pPairing, 224), alphay)
// beta2
mstore(add(_pPairing, 256), betax1)
mstore(add(_pPairing, 288), betax2)
mstore(add(_pPairing, 320), betay1)
mstore(add(_pPairing, 352), betay2)
// vk_x
mstore(add(_pPairing, 384), mload(add(pMem, pVk)))
mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32))))
// gamma2
mstore(add(_pPairing, 448), gammax1)
mstore(add(_pPairing, 480), gammax2)
mstore(add(_pPairing, 512), gammay1)
mstore(add(_pPairing, 544), gammay2)
// C
mstore(add(_pPairing, 576), calldataload(pC))
mstore(add(_pPairing, 608), calldataload(add(pC, 32)))
// delta2
mstore(add(_pPairing, 640), deltax1)
mstore(add(_pPairing, 672), deltax2)
mstore(add(_pPairing, 704), deltay1)
mstore(add(_pPairing, 736), deltay2)
let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20)
isOk := and(success, mload(_pPairing))
}
let pMem := mload(0x40)
mstore(0x40, add(pMem, pLastMem))
// Validate that all evaluations ∈ F
checkField(calldataload(add(_pubSignals, 0)))
checkField(calldataload(add(_pubSignals, 32)))
// Validate all evaluations
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)
result := isValid
}
}
}
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
contracts/RWAToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./AdminZamik.sol";
interface IKYCWhitelist {
function isWhitelisted(address account) external view returns (bool);
}
interface IUORSRegistry {
function authorizedUORS(address operater) external view returns (bool);
}
/**
* @title RWAToken
* @notice PRC-20 token representing a Real World Asset on PulseChain
* @dev Backed by verified off-chain data stored on IPFS.
* Minting requires a valid ZK proof from ZKVerifier.
* Transfer fee: 0.5% ob vsakem transferu → feeRecipient (platforma).
*
* OPCIJSKA KYC transfer-restrikcija (permissioned token):
* - če je `kyc` nastavljen (!= address(0)), morata biti POŠILJATELJ in
* PREJEMNIK na KYC beli listi (izjeme: mint, burn, fee-leg na feeRecipient).
* - To prepreči, da bi RWA pristal na nepreverjeni denarnici (zahteva, ki jo
* regulacija običajno postavlja finančnim instrumentom — MiFID II/ZTFI-1).
* - POMEMBNO: naslov Marketplace pogodbe (escrow listingov/ponudb) mora biti
* na beli listi, sicer list()/buy()/acceptOffer() padejo.
* - Privzeto IZKLOPLJENO (kyc == address(0)) → navaden prosto-prenosljiv ERC-20.
*/
/**
* @notice Nastavitve novega zetona v ENI strukturi.
* @dev Ne zaradi lepote: devet locenih parametrov konstruktorja je tovarno
* pognalo v "stack too deep" (Groth16 assembly + `new` z devetimi argumenti).
* Struktura gre v pomnilnik namesto na sklad.
*/
struct NastavitveZetona {
string ime;
string simbol;
string metapodatkiCID;
uint256 vrednostUSD;
address zkVerifier;
address entropyOracle;
address feeRecipient; // NIKOLI privzeto msg.sender — glej opombo pri konstruktorju
address kyc; // 0x0 = brez omejitve, a mora biti napisano izrecno
address lastnik;
address uors; // UORSRegistry — kdo sme sredstvo zamrzniti (glej spodaj)
address overitelj; // UORS operater, ki je sredstvo overil — glej `samoOveritelj`
}
contract RWAToken is ERC20, Ownable, AdminZamik {
string public assetMetadataCID;
uint256 public assetValueUSD;
address public zkVerifier;
address public entropyOracle;
// --- Provizije ---
uint256 public constant TRANSFER_FEE_BPS = 50; // 0,5% = 50 basis points
address public feeRecipient; // prejemnik provizij (platforma)
// ── Kdo je sredstvo overil ─────────────────────────────────────────────────
//
// Cenitev sme spremeniti SAMO ta naslov. Cenitev je izjava o vrednosti
// tujega premozenja in doloca ceno prve prodaje; odgovornost zanjo nosi
// tisti overitelj, ki je sredstvo overil, ne katerikoli drug.
//
// Zapise ga tovarna ob nastanku (`msg.sender` v `ustvariSredstvo`), zato je
// `immutable`: naknadno ga ne more prestaviti nihce, tudi lastnik ne.
address public immutable overitelj;
// ── Prvotni lastnik in enkratnost kovanja ─────────────────────────────────
//
// `prvotniLastnik` je naslov, ki je prejel prvo (in edino) kovanje tega
// sredstva. Trznica iz njega izpelje, ali je oglas PRIMARNI: primarna je
// prodaja tistega, ki je sredstvo dobil ob tokenizaciji, in samo zanjo velja
// omejitev, da mora biti cena enaka overjeni cenitvi. Ce bi to zastavico
// vpisoval prodajalec sam, bi bila omejitev obvod z enim argumentom.
//
// Vrednost `address(0)` pomeni "se ni skovano" in hkrati sluzi kot kljucavnica:
// kovanje je mogoce natanko enkrat. Enkratnost ni kozmetika — cena na primarnem
// trgu je `cenitev / stevilo enot`, zato bi vsako naknadno kovanje tiho premaknilo
// razmerje "ena enota = 1/N sredstva" vsem dosedanjim imetnikom.
address public prvotniLastnik;
// Dovoljenje za en sam `mintFee`, ki sledi kovanju. Ni javno: je notranje
// stanje ene transakcije, ne podatek o sredstvu.
bool private provizijaOdprta;
/// @dev Kolicina, skovana prvotnemu lastniku — zgornja meja za provizijo ob kovanju.
uint256 private skovanoLastniku;
// --- KYC transfer-restrikcija (opcijska) ---
address public kyc; // KYCWhitelist; če address(0), je restrikcija IZKLOPLJENA
// ─────────────────────────────────────────────────────────────────────────
// ZAPORA PRENOSA — varovalo za kupca
//
// Kovanje v denarnico lastnika sredstva je takojsnje; PRENOS komurkoli
// drugemu (in s tem oglas na trznici) je mogoc sele po `aktivenOd`.
// V tem oknu je sredstvo javno vidno in ga je mogoce ustaviti, preden je
// prenosljivo tretji osebi.
//
// Omejitev je v zetonu, ne v Marketplaceu: neposreden `transfer` iz
// denarnice bi jo sicer obsel.
//
// ⚠️ Dolzino zapore doloca VERIGA, ne clovek — enako kot `zamik()`:
// na produkcijski verigi 48 ur, drugod 10 minut, z ISTO bajtno kodo.
// Prej je bila `constant` 48 ur povsod, kar je na testnetu pomenilo dvodnevno
// cakanje pri vsakem preizkusu. Fiksna vrednost, ki bi jo bilo treba pred
// produkcijo rocno vrniti, bi bila zanasanje na spomin: tako je nihce ne more
// skrajsati in nihce je ne more pozabiti prestaviti nazaj.
//
// ⚠️ Ime NI ZAPORA_PRENOSA (velike crke): vrednost ni konstanta, ker je odvisna
// od verige. Ime, ki obljublja konstanto, bi bilo napacno poimenovana stevilka.
// ─────────────────────────────────────────────────────────────────────────
function _zaporaPrenosa() internal view returns (uint256) {
return block.chainid == MAINNET ? 48 hours : 10 minutes;
}
/// @notice Koliko casa po nastanku sredstvo se ni prenosljivo. Bere ga stran,
/// da lahko pove pravo stevilko na pravi verigi.
function zaporaPrenosa() external view returns (uint256) {
return _zaporaPrenosa();
}
uint256 public immutable aktivenOd;
// ─────────────────────────────────────────────────────────────────────────
// ZAMRZNITEV — pravico ima izkljucno pooblasceni UORS operater
//
// Obstoj sredstva potrdi UORS, zato je tudi preklic v njegovi pristojnosti.
// Lastnik (admin) tega zetona ne more zamrzniti; s tem bi presojal o
// posameznem sredstvu in prevzel del odgovornosti, ki je pri UORS.
//
// Posledica, ki je del zasnove: admin posameznega sredstva ne more ustaviti.
// Njegova pot je sistemska — odvzem pooblastila operaterju in `pause()` na
// ZKVerifierju oziroma Marketplaceu.
// ─────────────────────────────────────────────────────────────────────────
address public immutable uors; // UORSRegistry
bool public zamrznjen;
event Zamrznjeno(address indexed kdo);
event Odmrznjeno(address indexed kdo);
mapping(bytes32 => bool) public usedProofs;
// Dogodka za spremembo opisa ni: `assetMetadataCID` je nespremenljiv.
/// @notice Sprememba prijavljene vrednosti sredstva; oddana ob vsaki spremembi,
/// da je razvidna iz dnevnika verige in ne le iz stanja pogodbe.
event AssetValueUpdated(uint256 newValueUSD);
event TokensMinted(address indexed to, uint256 amount, bytes32 proofHash);
/// @notice Zapisan prvotni lastnik — iz njega trznica izpelje primarni oglas.
event PrvotniLastnikZapisan(address indexed lastnik);
event TransferFeeCollected(address indexed from, address indexed to, uint256 fee);
event FeeRecipientUpdated(address indexed newRecipient);
event KycUpdated(address indexed newKyc);
/**
* @notice `feeRecipient`, `kyc` in `lastnik` se nastavijo v konstruktorju,
* ne po postavitvi.
* @dev Nobena od teh vrednosti nima privzetka: `feeRecipient` in `lastnik`
* sta obvezna (`require` spodaj), `kyc` pa je lahko `address(0)` samo,
* ce je to izrecno podano. Zeton brez KYC omejitve je s tem zavestna
* izbira ob postavitvi in ne posledica neizvedenega naknadnega koraka.
*/
constructor(NastavitveZetona memory n)
ERC20(n.ime, n.simbol)
Ownable(n.lastnik)
{
require(n.zkVerifier != address(0), "ZKVerifier je nujen");
require(n.feeRecipient != address(0), "feeRecipient je nujen");
require(n.lastnik != address(0), "Lastnik je nujen");
assetMetadataCID = n.metapodatkiCID;
assetValueUSD = n.vrednostUSD;
zkVerifier = n.zkVerifier;
entropyOracle = n.entropyOracle;
feeRecipient = n.feeRecipient;
kyc = n.kyc; // sme biti 0x0, a mora biti napisano izrecno
uors = n.uors;
overitelj = n.overitelj; // 0x0 pomeni: cenitve ne more spremeniti nihce
aktivenOd = block.timestamp + _zaporaPrenosa();
}
modifier samoUORS() {
require(uors != address(0) && IUORSRegistry(uors).authorizedUORS(msg.sender),
"Samo UORS operater");
_;
}
/**
* @dev Samo TISTI overitelj, ki je sredstvo overil — in samo dokler je se
* pooblascen. Odvzem pooblastila mu pravico takoj vzame; skrbnik s tem
* obdrzi veto, ne da bi dobil moznost cenitev spremeniti sam.
*
* ⚠️ Trije LOCENI razlogi, ne en skupen: bralec mora vedeti, ali sredstvo
* overitelja nima, ali ga ima in to ni on, ali pa mu je bilo pooblastilo
* odvzeto. Nevednost in prepoved nista isto.
*/
modifier samoOveritelj() {
require(overitelj != address(0), "Sredstvo nima zapisanega overitelja");
require(msg.sender == overitelj, "Samo overitelj tega sredstva");
require(uors != address(0) && IUORSRegistry(uors).authorizedUORS(msg.sender),
"Overitelj ni vec pooblascen");
_;
}
/// @notice Ali je sredstvo mogoce prenasati (in s tem prodajati).
/// @dev Namenjeno strani, da lahko pove RAZLOG in odstevanje, namesto da
/// uporabnik dobi golo revertirano transakcijo brez pojasnila.
function jePrenosMogoc() external view returns (bool mogoc, uint256 odKdaj, bool zamrznjeno) {
return (block.timestamp >= aktivenOd && !zamrznjen, aktivenOd, zamrznjen);
}
/// @notice Zamrzne prenose tega sredstva. Takojsno — nevarnost ne caka.
function zamrzni() external samoUORS {
zamrznjen = true;
emit Zamrznjeno(msg.sender);
}
/// @notice Odmrzne. Prav tako samo UORS: kdor je odgovoren za izjavo, jo tudi
/// umakne in vrne.
function odmrzni() external samoUORS {
zamrznjen = false;
emit Odmrznjeno(msg.sender);
}
/**
* @notice Mintanje prek ZKVerifier (brez transfer fee — to je kreacija tokenov).
*/
function mintWithProof(address to, uint256 amount, bytes32 proofHash) external {
require(msg.sender == zkVerifier, "Only ZK verifier");
require(!usedProofs[proofHash], "Proof already used");
// Sredstvo se skuje natanko enkrat. Brez te omejitve bi lahko overitelj
// registriral novo kodo za isti zeton in obtok bi zrasel — s tem bi se
// razmerje "ena enota = 1/N sredstva" in primarna cena premaknila vsem.
require(prvotniLastnik == address(0), "Sredstvo je ze skovano");
usedProofs[proofHash] = true;
prvotniLastnik = to;
provizijaOdprta = true;
skovanoLastniku = amount;
_mint(to, amount);
emit TokensMinted(to, amount, proofHash);
emit PrvotniLastnikZapisan(to);
}
/**
* @notice Mintanje provizije ob tokenizaciji — pokliče samo ZKVerifier.
* @dev Brez usedProofs check — fee mint ne porabi proof.
*/
function mintFee(address to, uint256 amount) external {
require(msg.sender == zkVerifier, "Only ZK verifier");
require(to != address(0), "Zero address");
// Provizija sme nastati samo takoj za kovanjem in samo enkrat. Brez tega
// bi bila enkratnost kovanja odvisna od tega, kako se vede DRUGA pogodba
// (ZKVerifier), namesto da bi jo ta pogodba zagotavljala sama.
require(provizijaOdprta, "Provizija brez kovanja");
// Meja zneska: provizija 0,5 % od skupne kolicine je najvec 1/199 dela,
// ki ga dobi lastnik (fee = floor(g/200), neto = g - fee ⇒ 199·fee ≤ neto).
// Tako enkratnost in velikost obtoka drzita tudi, ce bi se ZKVerifier
// vedel drugace, kot je predvideno.
require(amount * 199 <= skovanoLastniku, "Provizija previsoka");
provizijaOdprta = false;
_mint(to, amount);
}
/**
* @notice Override _update: ob vsakem transferu (ne mint/burn) zaračuna 0,5% fee.
* @dev OZ v5 uporablja _update namesto _transfer/_beforeTokenTransfer.
* Fee se ne zaračuna pri: mint (from==0), burn (to==0),
* feeRecipient transakcijah (izogne se loop-u).
*/
function _update(address from, address to, uint256 amount) internal override {
bool isMint = (from == address(0));
bool isBurn = (to == address(0));
// --- KYC transfer-restrikcija (opcijska; aktivna le če kyc != address(0)) ---
if (kyc != address(0)) {
// Prejemnik mora biti preverjen (izjema: burn). Tudi feeRecipient je
// stranka kot vsaka druga in mora biti na beli listi; provizija ob
// prenosu gre mimo te preverbe, ker je zapisana s super._update.
if (!isBurn) {
require(IKYCWhitelist(kyc).isWhitelisted(to), "Prejemnik ni KYC preverjen");
}
// Pošiljatelj mora biti preverjen (izjema: mint).
if (!isMint) {
require(IKYCWhitelist(kyc).isWhitelisted(from), "Posiljatelj ni KYC preverjen");
}
}
// --- Zapora prenosa in zamrznitev ---
// Mint (from == 0) je dovoljen takoj: lastnik sredstva ni ogrozen, ker
// nihce se nicesar ni placal. Vse ostalo caka na aktivacijo.
if (!isMint) {
require(!zamrznjen, "Sredstvo je zamrznjeno");
require(block.timestamp >= aktivenOd, "Sredstvo se ni aktivno za prenos");
}
// Mint, burn ali fee-related prenos → brez dodatnega fee
if (isMint || isBurn || from == feeRecipient || to == feeRecipient) {
super._update(from, to, amount);
return;
}
uint256 fee = amount * TRANSFER_FEE_BPS / 10_000;
uint256 net = amount - fee;
super._update(from, feeRecipient, fee); // 0,5% → platforma
super._update(from, to, net); // 99,5% → prejemnik
emit TransferFeeCollected(from, to, fee);
}
// --- Admin ---
// Vse spodnje spremembe tecejo prek javne napovedi in casovnega zamika
// (glej AdminZamik.sol). Imetnik zetona tako vsako spremembo vidi priti in
// ima cas izstopiti — namesto da se zbudi v drugacen instrument.
/// @notice Nastavi KYCWhitelist za transfer-restrikcijo. address(0) = izklopljeno.
/// @dev POZOR: ob vklopu mora biti naslov Marketplace pogodbe na beli listi (escrow).
function napoviSetKyc(address _kyc) external onlyOwner {
_napovej(keccak256(abi.encode("setKyc", _kyc)), "RWAToken.setKyc");
}
function setKyc(address _kyc) external onlyOwner {
_porabi(keccak256(abi.encode("setKyc", _kyc)), "RWAToken.setKyc");
kyc = _kyc;
emit KycUpdated(_kyc);
}
function napoviSetFeeRecipient(address _feeRecipient) external onlyOwner {
require(_feeRecipient != address(0), "Zero address");
_napovej(keccak256(abi.encode("setFeeRecipient", _feeRecipient)), "RWAToken.setFeeRecipient");
}
function setFeeRecipient(address _feeRecipient) external onlyOwner {
require(_feeRecipient != address(0), "Zero address");
_porabi(keccak256(abi.encode("setFeeRecipient", _feeRecipient)), "RWAToken.setFeeRecipient");
feeRecipient = _feeRecipient;
emit FeeRecipientUpdated(_feeRecipient);
}
// ── Metapodatki (opis sredstva na IPFS) so NESPREMENLJIVI ──────────────────
//
// `assetMetadataCID` se nastavi ob postavitvi in ga ne more spremeniti nihce —
// ne lastnik in ne UORS operater; funkcije za to ni. Imetnik zato bere isti
// opis, na podlagi katerega je sredstvo pridobil.
//
// Posledici, ki sta del zasnove:
// 1. napake v opisu ni mogoce popraviti; popravek pomeni novo sredstvo,
// 2. vsebina zivi na IPFS in je dosegljiva, dokler je kdo pinja — CID na
// verigi doloca, KATERA vsebina je veljavna, ne zagotavlja pa hrambe.
// ── Cenitev sredstva: spremeni jo lahko SAMO UORS ──────────────────────────
//
// `assetValueUSD` je izjava o vrednosti realnega sredstva. Za tako izjavo
// odgovarja overitelj, ne skrbnik sistema; skrbnik lahko dejanje prepreci
// (`pause`, odvzem pooblastila), ne more pa ga opraviti namesto njega.
//
// Cenitev doloca tudi ceno, po kateri je sredstvo mogoce prvic prodati, zato
// bi pravica skrbnika pomenila, da skrbnik doloca prodajno ceno tujega
// sredstva. Sprememba gre skozi napoved in zamik, torej je javna vnaprej.
//
// ⚠️ Obe funkciji morata imeti ISTO omejitev. Ce bi eno napovedoval skrbnik
// in drugo izvajal overitelj, bi bila polovica postopka v napacnih rokah.
function napoviUpdateAssetValue(uint256 newValueUSD) external samoOveritelj {
_napovej(keccak256(abi.encode("updateAssetValue", newValueUSD)), "RWAToken.updateAssetValue");
}
function updateAssetValue(uint256 newValueUSD) external samoOveritelj {
_porabi(keccak256(abi.encode("updateAssetValue", newValueUSD)), "RWAToken.updateAssetValue");
assetValueUSD = newValueUSD;
emit AssetValueUpdated(newValueUSD);
}
/// @notice Onemogoceno: renounceOwnership() bi trajno ubil setKyc in
/// setFeeRecipient.
function renounceOwnership() public pure override {
revert("Renounce onemogocen");
}
}
contracts/RWATokenFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./RWAToken.sol";
interface IZKVerifierRegistracija {
function registrirajZeton(address zeton) external;
}
interface IUORSRegister {
function authorizedUORS(address operater) external view returns (bool);
}
/**
* @title RWATokenFactory
* @notice Postavi po en ERC-20 zeton za VSAKO realno sredstvo.
*
* EN ZETON NA SREDSTVO
* Vsako sredstvo ima svojo ERC-20 pogodbo. Deleži razlicnih sredstev tako niso
* medsebojno zamenljivi in je vrednost (`assetValueUSD`) lastnost posameznega
* sredstva, ne skupna vsem. Locitev je na verigi in je z metapodatki ni mogoce
* nadomestiti.
*
* ZAKAJ TOVARNA IN NE ROCNA POSTAVITEV
* Postavitev zetona zahteva vec zaporednih korakov (nastavitev prejemnika
* provizij in KYC seznama, prenos lastnistva, registracija pri ZKVerifierju).
* Tovarna jih opravi v eni transakciji, zato delno izveden postopek ni mogoc.
*
* KAJ TOVARNA NE POCNE
* Ne kuje zetonov. Ti nastanejo izkljucno prek ZK dokaza v ZKVerifierju;
* tovarna ustvari le pogodbo brez zaloge.
*/
contract RWATokenFactory is Ownable {
address public immutable zkVerifier;
address public immutable entropyOracle;
// ─────────────────────────────────────────────────────────────────────────
// KDO SME USTVARITI SREDSTVO
//
// Sredstvo sme ustvariti izkljucno pooblasceni UORS operater (overitelj), ne
// lastnik te pogodbe. Ustvaritev je izjava, da dolocen predmet obstaja, je
// ocenjen in ima lastnika; to preverjanje opravi overitelj.
//
// Lastnik pogodbe ni v poti nastanka niti kot sopodpisnik — sicer bi bil
// soizdajatelj in odgovornost za vsebino izjave bi bila deljena.
// ─────────────────────────────────────────────────────────────────────────
address public immutable uors; // UORSRegistry
/// Skupne nastavitve, ki jih dobi vsak nov zeton.
address public feeRecipient;
address public kyc;
address[] public sredstva;
mapping(address => bool) public jeNase;
event SredstvoUstvarjeno(
address indexed zeton,
string ime,
string simbol,
uint256 vrednostUSD,
string metapodatkiCID
);
event NastavitveSpremenjene(address feeRecipient, address kyc);
modifier samoUORS() {
require(IUORSRegister(uors).authorizedUORS(msg.sender), "Samo UORS operater");
_;
}
constructor(
address _zkVerifier,
address _entropyOracle,
address _feeRecipient,
address _kyc,
address _lastnik,
address _uors
) Ownable(_lastnik) {
require(_zkVerifier != address(0), "ZKVerifier je nujen");
require(_feeRecipient != address(0), "feeRecipient je nujen");
require(_lastnik != address(0), "Lastnik je nujen");
require(_uors != address(0), "UORSRegistry je nujen");
uors = _uors;
zkVerifier = _zkVerifier;
entropyOracle = _entropyOracle;
feeRecipient = _feeRecipient;
kyc = _kyc;
}
/**
* @notice Ustvari novo sredstvo — en zeton, ena stvar iz resnicnega sveta.
* @dev Vsako sredstvo je zavesten podpis lastnika. Za regulirano dejavnost je
* to prednost, ne ovira: namerno dejanje na sredstvo je bolje kot tiho
* dodajanje v skupno posodo.
*/
function ustvariSredstvo(
string calldata ime,
string calldata simbol,
string calldata metapodatkiCID,
uint256 vrednostUSD
) external samoUORS returns (address zeton) {
require(bytes(ime).length != 0, "Ime je nujno");
require(bytes(simbol).length != 0, "Simbol je nujen");
require(vrednostUSD != 0, "Vrednost je nujna");
zeton = address(new RWAToken(NastavitveZetona({
ime: ime,
simbol: simbol,
metapodatkiCID: metapodatkiCID,
vrednostUSD: vrednostUSD,
zkVerifier: zkVerifier,
entropyOracle: entropyOracle,
feeRecipient: feeRecipient,
kyc: kyc,
lastnik: owner(), // lastnik zetona = lastnik tovarne (Ledger / multisig)
uors: uors, // zamrznitev sredstva sme SAMO UORS (glej RWAToken)
// Overitelj je TISTI operater, ki sredstvo ustvarja zdaj. Zapise se
// nespremenljivo in doloca, kdo sme pozneje spremeniti cenitev —
// odgovornost za izjavo o vrednosti nosi tisti, ki jo je dal.
overitelj: msg.sender
})));
sredstva.push(zeton);
jeNase[zeton] = true;
// Sele s tem sme ZKVerifier kovati na tem zetonu.
IZKVerifierRegistracija(zkVerifier).registrirajZeton(zeton);
emit SredstvoUstvarjeno(zeton, ime, simbol, vrednostUSD, metapodatkiCID);
}
/// @notice Spremeni nastavitve za PRIHODNJE zetone. Ze ustvarjenih ne dotakne —
/// tam se feeRecipient in kyc spreminjata na zetonu samem, pod zamikom.
function nastavi(address _feeRecipient, address _kyc) external onlyOwner {
require(_feeRecipient != address(0), "feeRecipient je nujen");
feeRecipient = _feeRecipient;
kyc = _kyc;
emit NastavitveSpremenjene(_feeRecipient, _kyc);
}
function steviloSredstev() external view returns (uint256) {
return sredstva.length;
}
/// @notice Vsa sredstva naenkrat — stran jih rabi za seznam zetonov.
function vsaSredstva() external view returns (address[] memory) {
return sredstva;
}
/// @notice Onemogoceno: brez lastnika tovarna ne bi mogla vec ustvariti sredstva.
function renounceOwnership() public pure override {
revert("Renounce onemogocen");
}
}
@openzeppelin/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
@openzeppelin/contracts/interfaces/draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-721.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
@openzeppelin/contracts/access/Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
contracts/AdminZamik.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title AdminZamik
* @notice Casovni zamik na nevarne admin spremembe.
*
* ZAKAJ TO OBSTAJA
* ----------------
* Owner kljuc na tej platformi NE more krasti: ne more kovati zetonov, ne more
* premikati tujih zetonov in ne more v escrow. Kar PA lahko, je tiho zamenjati
* nastavitve pod nogami uporabnikov — KYC listo, placilni token, prijavljeno
* vrednost sredstva.
*
* Vsi trije so napadi, ki jih je mogoce VIDETI VNAPREJ. Zato jih ne branimo z
* multisigom (ki varuje lastnika kljuca), ampak s casovnim zamikom (ki varuje
* UPORABNIKA): sprememba se mora najprej javno napovedati, sele po zamiku jo je
* mogoce izvesti. Uporabnik ima ves ta cas, da spremembo vidi in izstopi.
*
* Namenoma NI zunanje timelock pogodbe kot lastnika. Tak vzorec doda tveganje
* nedosegljivosti: ce lastniku kdaj ne uspe sestaviti transakcije skozi tujo
* pogodbo, so kontrakti zamrznjeni za vedno. Tu je zamik vgrajen, lastnik pa
* ostane navadna denarnica.
*
* KAJ ZAMIK NE POKRIVA (namerno)
* ------------------------------
* - pause() — zasilna zavora mora biti takojsnja, sicer ni zavora
* - unpause() — vrnitev v obratovanje naj bo hitra
* - odvzem pravice zaustavljalca — ob razkritem kljucu ga moras vreci TAKOJ
* Nevarnost caka, zasilna zavora ne.
*/
abstract contract AdminZamik is Ownable {
/// @notice Rok, v katerem je napoved uporabna. Po njem potece.
/// @dev Brez tega bi stara napoved lezala na verigi kot nabita puska:
/// napoves nekaj neopaznega danes, izvedes cez pol leta, ko nihce vec
/// ne gleda. Napoved, ki ni bila izvedena v roku, je treba ponoviti.
uint256 public constant ROK = 7 days;
/// @notice id napovedi → cas, od katerega je izvedljiva (0 = ni napovedi)
mapping(bytes32 => uint256) public izvedljivoOd;
event SpremembaNapovedana(bytes32 indexed id, string kaj, uint256 izvedljivoOd, uint256 potece);
event SpremembaIzvedena(bytes32 indexed id, string kaj);
event SpremembaPreklicana(bytes32 indexed id, string kaj);
/// @notice PulseChain mainnet. Edina veriga, kjer zamik varuje pravo vrednost.
uint256 internal constant MAINNET = 369;
/// @notice Koliko casa mora napovedana sprememba cakati, preden je izvedljiva.
///
/// mainnet (369) 48 ur imetnik ima cas spremembo videti in izstopiti
/// druge verige 10 minut razvojno okolje
///
/// @dev Vrednost doloca `block.chainid`, ne nastavitev ob postavitvi. Ista
/// bajtna koda tako na produkcijski verigi vedno vrne 48 ur; razvojne
/// vrednosti ni mogoce prenesti na mainnet, niti pomotoma.
/// @dev `virtual` zato, da lahko posamezna testna pogodba vrednost povozi.
function zamik() public view virtual returns (uint256) {
return block.chainid == MAINNET ? 48 hours : 10 minutes;
}
// ── Notranje ────────────────────────────────────────────────────────────
/// @dev id MORA vsebovati tocne parametre spremembe, sicer bi lahko
/// napovedal nedolzno vrednost in izvedel skodljivo.
function _napovej(bytes32 id, string memory kaj) internal {
require(izvedljivoOd[id] == 0, "Ze napovedano");
uint256 od = block.timestamp + zamik();
izvedljivoOd[id] = od;
emit SpremembaNapovedana(id, kaj, od, od + ROK);
}
function _porabi(bytes32 id, string memory kaj) internal {
uint256 od = izvedljivoOd[id];
require(od != 0, "Ni napovedano");
require(block.timestamp >= od, "Zamik se ni potekel");
require(block.timestamp <= od + ROK, "Napoved je potekla");
delete izvedljivoOd[id];
emit SpremembaIzvedena(id, kaj);
}
// ── Javno ───────────────────────────────────────────────────────────────
/// @notice Preklic napovedane spremembe. Takojsen — premisliti se mora biti
/// mogoce hitreje, kot se izvede.
function prekliciSpremembo(bytes32 id, string calldata kaj) external onlyOwner {
require(izvedljivoOd[id] != 0, "Ni napovedano");
delete izvedljivoOd[id];
emit SpremembaPreklicana(id, kaj);
}
/// @notice Stanje napovedi — da ga stran in nadzorni sistem lahko prikazeta.
function stanjeNapovedi(bytes32 id)
external
view
returns (bool napovedana, uint256 od, uint256 potece, bool izvedljivaZdaj)
{
uint256 t = izvedljivoOd[id];
if (t == 0) return (false, 0, 0, false);
return (true, t, t + ROK, block.timestamp >= t && block.timestamp <= t + ROK);
}
}
@openzeppelin/contracts/token/ERC20/ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/// @inheritdoc IERC20
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation sets the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the `transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}
@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity >=0.6.2;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
Compiler Settings
{"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":200,"enabled":true},"evmVersion":"paris"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_entropyOracle","internalType":"address"}]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"event","name":"AssetMinted","inputs":[{"type":"uint256","name":"commitment","internalType":"uint256","indexed":true},{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"address","name":"zeton","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AssetRegistered","inputs":[{"type":"uint256","name":"commitment","internalType":"uint256","indexed":true},{"type":"address","name":"zeton","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AssetUnregistered","inputs":[{"type":"uint256","name":"commitment","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"MintFeeCollected","inputs":[{"type":"address","name":"feeRecipient","internalType":"address","indexed":true},{"type":"uint256","name":"fee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ProofVerified","inputs":[{"type":"address","name":"claimant","internalType":"address","indexed":true},{"type":"bytes32","name":"proofHash","internalType":"bytes32","indexed":false}],"anonymous":false},{"type":"event","name":"SpremembaIzvedena","inputs":[{"type":"bytes32","name":"id","internalType":"bytes32","indexed":true},{"type":"string","name":"kaj","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"SpremembaNapovedana","inputs":[{"type":"bytes32","name":"id","internalType":"bytes32","indexed":true},{"type":"string","name":"kaj","internalType":"string","indexed":false},{"type":"uint256","name":"izvedljivoOd","internalType":"uint256","indexed":false},{"type":"uint256","name":"potece","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SpremembaPreklicana","inputs":[{"type":"bytes32","name":"id","internalType":"bytes32","indexed":true},{"type":"string","name":"kaj","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"Sproscen","inputs":[{"type":"address","name":"kdo","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"TovarnaNastavljena","inputs":[{"type":"address","name":"tovarna","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"UORSAuthorized","inputs":[{"type":"address","name":"uors","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"UORSRevoked","inputs":[{"type":"address","name":"uors","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ZaustavljalecSpremenjen","inputs":[{"type":"address","name":"kdo","internalType":"address","indexed":true},{"type":"bool","name":"lahko","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"Zaustavljeno","inputs":[{"type":"address","name":"kdo","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ZetonOdvzet","inputs":[{"type":"address","name":"zeton","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ZetonPooblascen","inputs":[{"type":"address","name":"zeton","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MINT_FEE_BPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ROK","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"authorizeUORS","inputs":[{"type":"address","name":"uors","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"authorizedUORS","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"commitmentUsed","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"commitmentZeton","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"dodajZeton","inputs":[{"type":"address","name":"zeton","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"entropyOracle","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"izvedljivoOd","inputs":[{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"jeZaustavljalec","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"napoviAuthorizeUORS","inputs":[{"type":"address","name":"uors","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"napoviDodajZeton","inputs":[{"type":"address","name":"zeton","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"napoviNastaviTovarno","inputs":[{"type":"address","name":"_tovarna","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"nastaviTovarno","inputs":[{"type":"address","name":"_tovarna","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"nastaviZaustavljalca","inputs":[{"type":"address","name":"kdo","internalType":"address"},{"type":"bool","name":"lahko","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"odvzemiZeton","inputs":[{"type":"address","name":"zeton","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"pause","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"paused","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"pooblasceniZetoni","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"prekliciSpremembo","inputs":[{"type":"bytes32","name":"id","internalType":"bytes32"},{"type":"string","name":"kaj","internalType":"string"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"registerCommitment","inputs":[{"type":"uint256","name":"commitment","internalType":"uint256"},{"type":"address","name":"zeton","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"registerCommitments","inputs":[{"type":"uint256[]","name":"commitments","internalType":"uint256[]"},{"type":"address","name":"zeton","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"registeredCommitments","inputs":[{"type":"uint256","name":"commitment","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"registrirajZeton","inputs":[{"type":"address","name":"zeton","internalType":"address"}]},{"type":"function","stateMutability":"pure","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeUORS","inputs":[{"type":"address","name":"uors","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"napovedana","internalType":"bool"},{"type":"uint256","name":"od","internalType":"uint256"},{"type":"uint256","name":"potece","internalType":"uint256"},{"type":"bool","name":"izvedljivaZdaj","internalType":"bool"}],"name":"stanjeNapovedi","inputs":[{"type":"bytes32","name":"id","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"tovarna","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unpause","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unregisterCommitment","inputs":[{"type":"uint256","name":"commitment","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"verifyAndMint","inputs":[{"type":"uint256[2]","name":"_pA","internalType":"uint256[2]"},{"type":"uint256[2][2]","name":"_pB","internalType":"uint256[2][2]"},{"type":"uint256[2]","name":"_pC","internalType":"uint256[2]"},{"type":"uint256[2]","name":"_pubSignals","internalType":"uint256[2]"},{"type":"address","name":"zeton","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"zamik","inputs":[]}]
Contract Creation Code
0x60806040523480156200001157600080fd5b506040516200261a3803806200261a8339810160408190526200003491620000dd565b33806200005b57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b62000066816200008d565b50600280546001600160a01b0319166001600160a01b03929092169190911790556200010f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060208284031215620000f057600080fd5b81516001600160a01b03811681146200010857600080fd5b9392505050565b6124fb806200011f6000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c8063715018a61161011a5780639db2f571116100ad578063cd2b1a5f1161007c578063cd2b1a5f146104da578063d48693d1146104ed578063e5236f86146104f5578063e826f69a14610508578063f2fde38b1461051257600080fd5b80639db2f57114610489578063a2a230131461049c578063a9a9855d146104af578063c714d0f7146104d257600080fd5b80638456cb59116100e95780638456cb591461044a578063847561f8146104525780638da5cb5b146104655780639bcc6e7e1461047657600080fd5b8063715018a6146103e557806376e382c8146103ed578063776528cd146104005780637af89ef51461043757600080fd5b806328ce0d861161019d5780634a2ddbd11161016c5780634a2ddbd11461036c5780634d63ce361461037f5780635c975abb14610392578063666ddb231461039f578063704c1796146103c257600080fd5b806328ce0d8614610315578063365a0ca5146103285780633f4ba83a1461035157806344b0eb021461035957600080fd5b80630e78931d116101d95780630e78931d146102995780631446b861146102c457806319ee63c1146102d7578063235a0b9b146102ea57600080fd5b806305e105b91461020b5780630854eef5146102435780630a5b2df1146102715780630d8227da14610286575b600080fd5b61022e610219366004611f9b565b60066020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b610263610251366004611f9b565b60016020526000908152604090205481565b60405190815260200161023a565b61028461027f366004611fc9565b610525565b005b610284610294366004611fc9565b6105b7565b6002546102ac906001600160a01b031681565b6040516001600160a01b03909116815260200161023a565b6102846102d2366004611fc9565b610690565b6102846102e5366004611fc9565b610767565b61022e6102f8366004611f9b565b6000908152600560205260409020546001600160a01b0316151590565b610284610323366004611f9b565b6107d6565b6102ac610336366004611f9b565b6005602052600090815260409020546001600160a01b031681565b6102846108c7565b610284610367366004611fc9565b610906565b61028461037a366004611fed565b610994565b61028461038d36600461202b565b610a21565b60085461022e9060ff1681565b61022e6103ad366004611fc9565b60036020526000908152604090205460ff1681565b61022e6103d0366004611fc9565b60076020526000908152604090205460ff1681565b610284610bd1565b6102846103fb3660046120b1565b610c0f565b61041361040e366004611f9b565b610cb6565b6040805194151585526020850193909352918301521515606082015260800161023a565b610284610445366004611fc9565b610d23565b610284610d74565b610284610460366004611fc9565b610e22565b6000546001600160a01b03166102ac565b6004546102ac906001600160a01b031681565b61028461049736600461212d565b610efc565b6102846104aa366004611fc9565b611079565b61022e6104bd366004611fc9565b60096020526000908152604090205460ff1681565b610263603281565b6102846104e8366004612169565b611108565b610263611768565b610284610503366004611fc9565b61178a565b61026362093a8081565b610284610520366004611fc9565b611818565b61052d611853565b6001600160a01b03811660009081526003602052604090205460ff1661056e5760405162461bcd60e51b8152600401610565906121e8565b60405180910390fd5b6001600160a01b038116600081815260036020526040808220805460ff19169055517f513cdc19277bafa59b350daee3326371868289df4611fab72ab9749643ad7fef9190a250565b6105bf611853565b6001600160a01b0381166105e55760405162461bcd60e51b815260040161056590612215565b610646816040516020016105f9919061223b565b60405160208183030381529060405280519060200120604051806040016040528060198152602001785a4b56657269666965722e6e617374617669546f7661726e6f60381b815250611882565b600480546001600160a01b0319166001600160a01b0383169081179091556040517fe364cf452581b78d64b23d0370f72ba8380777f0a90e52e223986e17f014382790600090a250565b610698611853565b6001600160a01b0381166106be5760405162461bcd60e51b815260040161056590612215565b61071b816040516020016106d29190612274565b60405160208183030381529060405280519060200120604051806040016040528060158152602001742d25ab32b934b334b2b9173237b230b52d32ba37b760591b815250611882565b6001600160a01b038116600081815260036020526040808220805460ff19166001179055517f2e55dec88f978eb8229e3f08822286f2c8befca5884b9de310678253f6d171469190a250565b6004546001600160a01b031633146107b05760405162461bcd60e51b815260206004820152600c60248201526b53616d6f20746f7661726e6160a01b6044820152606401610565565b6001600160a01b03811661071b5760405162461bcd60e51b815260040161056590612215565b6107de611853565b6000818152600560205260409020546001600160a01b03166108335760405162461bcd60e51b815260206004820152600e60248201526d139bdd081c9959da5cdd195c995960921b6044820152606401610565565b60008181526006602052604090205460ff16156108815760405162461bcd60e51b815260206004820152600c60248201526b105b1c9958591e481d5cd95960a21b6044820152606401610565565b60008181526005602052604080822080546001600160a01b03191690555182917f99088e6d10d683bcaedea1a362ff6ebb8ab7d7c38f4e8c852e68161ae4cde7d391a250565b6108cf611853565b6008805460ff1916905560405133907f3a4aad69711d2e0c9ee20d814b95ca020a6a23491162ca2396215a432bbe773990600090a2565b61090e611853565b6001600160a01b0381166109345760405162461bcd60e51b815260040161056590612215565b610991816040516020016109489190612274565b60405160208183030381529060405280519060200120604051806040016040528060158152602001742d25ab32b934b334b2b9173237b230b52d32ba37b760591b8152506119ab565b50565b61099c611853565b6001600160a01b0382166109c25760405162461bcd60e51b815260040161056590612215565b6001600160a01b038216600081815260096020908152604091829020805460ff191685151590811790915591519182527f9562c728fd52688e7dc1a376e3ee2035e842ac8dff05803952c69492f957d88a910160405180910390a25050565b3360009081526007602052604090205460ff16610a715760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610565565b6001600160a01b03811660009081526003602052604090205460ff16610aa95760405162461bcd60e51b8152600401610565906121e8565b60005b82811015610bcb576000848483818110610ac857610ac86122a9565b90506020020135905080600003610b135760405162461bcd60e51b815260206004820152600f60248201526e16995c9bc818dbdb5b5a5d1b595b9d608a1b6044820152606401610565565b6000818152600560205260409020546001600160a01b031615610b6d5760405162461bcd60e51b8152602060048201526012602482015271105b1c9958591e481c9959da5cdd195c995960721b6044820152606401610565565b60008181526005602052604080822080546001600160a01b0319166001600160a01b0387169081179091559051909183917f85819f62de3f36778ef25274607ec7071cf41151e6e04a29971d86f30e8bbf919190a350600101610aac565b50505050565b60405162461bcd60e51b81526020600482015260136024820152722932b737bab731b29037b732b6b7b3b7b1b2b760691b6044820152606401610565565b610c17611853565b6000838152600160205260408120549003610c645760405162461bcd60e51b815260206004820152600d60248201526c4e69206e61706f766564616e6f60981b6044820152606401610565565b600083815260016020526040808220919091555183907f72003eda7c2c39777210bccbf69141a92bbe56d746914a16f5ee3aaf5e6f7a8390610ca990859085906122bf565b60405180910390a2505050565b600081815260016020526040812054819081908190808203610ce657600080600080945094509450945050610d1c565b600181610cf662093a8082612304565b834210158015610d125750610d0e62093a8085612304565b4211155b9450945094509450505b9193509193565b610d2b611853565b6001600160a01b038116600081815260076020526040808220805460ff19169055517fd84b626bc6cd1f6d62d5e197f48869d2a5ef1a750b360ad929c3de63c88bdfe49190a250565b6000546001600160a01b0316331480610d9c57503360009081526009602052604090205460ff165b610de85760405162461bcd60e51b815260206004820152601c60248201527f4e6920706f6f626c617363656e207a61207a61757374617669746576000000006044820152606401610565565b6008805460ff1916600117905560405133907f65b3009f81d789a2064493bfd78059e5eaacca20215307e85f985bf0e513ebd690600090a2565b610e2a611853565b6001600160a01b038116610e505760405162461bcd60e51b815260040161056590612215565b610eb081604051602001610e649190612317565b60405160208183030381529060405280519060200120604051806040016040528060188152602001775a4b56657269666965722e617574686f72697a65554f525360401b815250611882565b6001600160a01b038116600081815260076020526040808220805460ff19166001179055517fb334427bfaed9992168ed99fda266e2d7cf5c2ea1a37a1f76e82fd1e307edf7f9190a250565b3360009081526007602052604090205460ff16610f4c5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610565565b81600003610f8e5760405162461bcd60e51b815260206004820152600f60248201526e16995c9bc818dbdb5b5a5d1b595b9d608a1b6044820152606401610565565b6001600160a01b03811660009081526003602052604090205460ff16610fc65760405162461bcd60e51b8152600401610565906121e8565b6000828152600560205260409020546001600160a01b0316156110205760405162461bcd60e51b8152602060048201526012602482015271105b1c9958591e481c9959da5cdd195c995960721b6044820152606401610565565b60008281526005602052604080822080546001600160a01b0319166001600160a01b0385169081179091559051909184917f85819f62de3f36778ef25274607ec7071cf41151e6e04a29971d86f30e8bbf919190a35050565b611081611853565b6001600160a01b0381166110a75760405162461bcd60e51b815260040161056590612215565b610991816040516020016110bb919061223b565b60405160208183030381529060405280519060200120604051806040016040528060198152602001785a4b56657269666965722e6e617374617669546f7661726e6f60381b8152506119ab565b33816111445760405162461bcd60e51b815260206004820152600b60248201526a16995c9bc8185b5bdd5b9d60aa1b6044820152606401610565565b60085460ff16156111975760405162461bcd60e51b815260206004820152601760248201527f4b6f76616e6a65206a65207a6175737461766c6a656e6f0000000000000000006044820152606401610565565b6001600160a01b03831660009081526003602052604090205460ff166111cf5760405162461bcd60e51b8152600401610565906121e8565b83356000818152600560205260409020546001600160a01b0385811691161461123a5760405162461bcd60e51b815260206004820152601960248201527f436f6d6d69746d656e74206e69207a61207461207a65746f6e000000000000006044820152606401610565565b60008181526006602052604090205460ff16156112905760405162461bcd60e51b8152602060048201526014602482015273105cdcd95d08185b1c9958591e481b5a5b9d195960621b6044820152606401610565565b60208501357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181106113045760405162461bcd60e51b815260206004820152601860248201527f4a61766e69207369676e616c20697a76656e20706f6c6a6100000000000000006044820152606401610565565b6000811161134d5760405162461bcd60e51b81526020600482015260166024820152750446f6b617a616e6120767265646e6f7374206a6520360541b6044820152606401610565565b670de0b6b3a7640000856001600160a01b031663cde2202a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611394573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b8919061234f565b6113c29190612368565b81111561141f5760405162461bcd60e51b815260206004820152602560248201527f446f6b617a616e6120767265646e6f73742070726573656761207072696a61766044820152646c6a656e6f60d81b6064820152608401610565565b8068056bc75e2d6310000061143560018761238a565b61143f9190612368565b106114975760405162461bcd60e51b815260206004820152602260248201527f4b6f6c6963696e61207072657365676120646f6b617a616e6f20767265646e6f6044820152611cdd60f21b6064820152608401610565565b6114a389898989611a5e565b6114e25760405162461bcd60e51b815260206004820152601060248201526f24b73b30b634b2102d2590383937b7b360811b6044820152606401610565565b6000828152600660209081526040808320805460ff191660011790556005909152812080546001600160a01b03191690556115228a8a8a8a888a8c611f07565b9050600061271061153460328861239d565b61153e9190612368565b90506001600160a01b038716636a3b5c4d8661155a848a61238a565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260448101859052606401600060405180830381600087803b1580156115a757600080fd5b505af11580156115bb573d6000803e3d6000fd5b505050506000876001600160a01b031663469048406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162391906123b4565b604051638cc9d10760e01b81526001600160a01b0380831660048301526024820185905291925090891690638cc9d10790604401600060405180830381600087803b15801561167157600080fd5b505af1158015611685573d6000803e3d6000fd5b50505050806001600160a01b03167fd2b7c9cdd8f0b876462485c766dac30078e9ef6f76ce1e57ca92c7bd553bcf1f836040516116c491815260200190565b60405180910390a26001600160a01b03808916908716867f1365c616f24c2b6838dd9a07b147b6ffd5e232d55f3ffbd59cddbb073def4e92611706868c61238a565b60405190815260200160405180910390a4856001600160a01b03167f0565033f3973b38ac07b3b39f05c0bcfa1449a22d6fd347fdc63c7a8370e106a8460405161175291815260200190565b60405180910390a2505050505050505050505050565b6000610171461461177b57610258611780565b6202a3005b62ffffff16905090565b611792611853565b6001600160a01b0381166117b85760405162461bcd60e51b815260040161056590612215565b610991816040516020016117cc9190612317565b60405160208183030381529060405280519060200120604051806040016040528060188152602001775a4b56657269666965722e617574686f72697a65554f525360401b8152506119ab565b611820611853565b6001600160a01b03811661184a57604051631e4fbdf760e01b815260006004820152602401610565565b61099181611f4b565b6000546001600160a01b031633146118805760405163118cdaa760e01b8152336004820152602401610565565b565b600082815260016020526040812054908190036118d15760405162461bcd60e51b815260206004820152600d60248201526c4e69206e61706f766564616e6f60981b6044820152606401610565565b804210156119175760405162461bcd60e51b815260206004820152601360248201527216985b5a5ac81cd9481b9a481c1bdd195ad95b606a1b6044820152606401610565565b61192462093a8082612304565b4211156119685760405162461bcd60e51b81526020600482015260126024820152714e61706f766564206a6520706f74656b6c6160701b6044820152606401610565565b600083815260016020526040808220919091555183907ffac4537038ebc9eaa00d86a88a6c2e268012d306f6442966aab36014a195432390610ca9908590612417565b600082815260016020526040902054156119f75760405162461bcd60e51b815260206004820152600d60248201526c5a65206e61706f766564616e6f60981b6044820152606401610565565b6000611a01611768565b611a0b9042612304565b60008481526001602052604090208190559050827f63a0d065f46e07e30a4f246d28eb53f839982100439173db92dbd5700ab37c158383611a4f62093a8082612304565b604051610ca99392919061242a565b6000611ecb565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181106109915760008052600080fd5b600060405183815284602082015285604082015260408160608360076107d05a03fa915081611ac75760008052600080fd5b825160408201526020830151606082015260408360808360066107d05a03fa91505080611af75760008052600080fd5b5050505050565b7f0bbf368c7f6fa7f6559bf65da45c33b505a21ff7fb0874c3bd0e2e7baa1b170385527f05c6418bd08aff1241cc2850606a69d8b73a6d9875e25df448f52ae21907c432602086015260006080860186611b9b87357f181032dd16b7eea417d83a9181ab6887bae84d538a8731a6f3003db177c0caba7f083a4c6477d7d1a1feb5f0ba3ddcd764a617b31c182138d6e756100407c25c8984611a95565b611beb60208801357f19f2f485618fdb703d11b2fbc13e61ce7371c4f94e99c148b2d2d8e3f1d9a6547f1a5a3f66f163e8eb3a5351b30773f2cf6126755e91973e02ab7bd68d7752583784611a95565b50823581527f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760208401357f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4703066020820152833560408201526020840135606082015260408401356080820152606084013560a08201527f2d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e260c08201527f14bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d192660e08201527f0967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c6101008201527f0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab6101208201527f304cfbd1e08a704a99f5e847d93f8c3caafddec46b7a0d379da69a4d112346a76101408201527f1739c1b1a457a8c7313123d24d2f9192f896b7c63eea05a9d57f06547ad0cec8610160820152600087015161018082015260206000018701516101a08201527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26101c08201527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6101e08201527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b6102008201527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa610220820152843561024082015260208501356102608201527f10e115799fb9e210b69c6459d325cb70991b1a5f7b23166b9cb1079a84ec05796102808201527f11367b8fccc2b903fc7b4fa13cccc3bd581b6ae5bb74403ff1449df18caeb0616102a08201527f2bc521f6593c958f19d068ab6c9d871471993c59807984ff5540d2ae59f7ad6c6102c08201527f2bf6b0d953792ab8a4e7926e629078462da9a25f808b3d5c13c56b035c14b3f06102e08201526020816103008360086107d05a03fa9051169695505050505050565b6040516103808101604052611ee36000840135611a65565b611ef06020840135611a65565b611efd818486888a611afe565b9695505050505050565b60008787878787878746604051602001611f2898979695949392919061244f565b604051602081830303815290604052805190602001209050979650505050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060208284031215611fad57600080fd5b5035919050565b6001600160a01b038116811461099157600080fd5b600060208284031215611fdb57600080fd5b8135611fe681611fb4565b9392505050565b6000806040838503121561200057600080fd5b823561200b81611fb4565b91506020830135801515811461202057600080fd5b809150509250929050565b60008060006040848603121561204057600080fd5b833567ffffffffffffffff8082111561205857600080fd5b818601915086601f83011261206c57600080fd5b81358181111561207b57600080fd5b8760208260051b850101111561209057600080fd5b602092830195509350508401356120a681611fb4565b809150509250925092565b6000806000604084860312156120c657600080fd5b83359250602084013567ffffffffffffffff808211156120e557600080fd5b818601915086601f8301126120f957600080fd5b81358181111561210857600080fd5b87602082850101111561211a57600080fd5b6020830194508093505050509250925092565b6000806040838503121561214057600080fd5b82359150602083013561202081611fb4565b806040810183101561216357600080fd5b92915050565b600080600080600080610180878903121561218357600080fd5b61218d8888612152565b955060c08701888111156121a057600080fd5b6040880195506121b08982612152565b9450506121c1886101008901612152565b92506101408701356121d281611fb4565b8092505061016087013590509295509295509295565b6020808252601390820152722d32ba37b7103734903837b7b13630b9b1b2b760691b604082015260600190565b6020808252600c908201526b5a65726f206164647265737360a01b604082015260600190565b6040808252600e908201526d6e617374617669546f7661726e6f60901b60608201526001600160a01b0391909116602082015260800190565b6040808252600a90820152693237b230b52d32ba37b760b11b60608201526001600160a01b0391909116602082015260800190565b634e487b7160e01b600052603260045260246000fd5b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115612163576121636122ee565b6040808252600d908201526c617574686f72697a65554f525360981b60608201526001600160a01b0391909116602082015260800190565b60006020828403121561236157600080fd5b5051919050565b60008261238557634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115612163576121636122ee565b8082028115828204841417612163576121636122ee565b6000602082840312156123c657600080fd5b8151611fe681611fb4565b6000815180845260005b818110156123f7576020818501810151868301820152016123db565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000611fe660208301846123d1565b60608152600061243d60608301866123d1565b60208301949094525060400152919050565b6101c081016040808b84378083018a60005b600281101561247e57838284379183019190830190600101612461565b505050808960c08501378088610100850137506001600160a01b03958616610140830152610160820194909452919093166101808201526101a0019190915294935050505056fea26469706673582212203865fd61411c648c2f358d2522bed2471f1d9664536a68862fc5374b8e98db1864736f6c63430008180033000000000000000000000000f119beb1b343e46c4ed0e78362cd3f87e87ef4cc
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106102065760003560e01c8063715018a61161011a5780639db2f571116100ad578063cd2b1a5f1161007c578063cd2b1a5f146104da578063d48693d1146104ed578063e5236f86146104f5578063e826f69a14610508578063f2fde38b1461051257600080fd5b80639db2f57114610489578063a2a230131461049c578063a9a9855d146104af578063c714d0f7146104d257600080fd5b80638456cb59116100e95780638456cb591461044a578063847561f8146104525780638da5cb5b146104655780639bcc6e7e1461047657600080fd5b8063715018a6146103e557806376e382c8146103ed578063776528cd146104005780637af89ef51461043757600080fd5b806328ce0d861161019d5780634a2ddbd11161016c5780634a2ddbd11461036c5780634d63ce361461037f5780635c975abb14610392578063666ddb231461039f578063704c1796146103c257600080fd5b806328ce0d8614610315578063365a0ca5146103285780633f4ba83a1461035157806344b0eb021461035957600080fd5b80630e78931d116101d95780630e78931d146102995780631446b861146102c457806319ee63c1146102d7578063235a0b9b146102ea57600080fd5b806305e105b91461020b5780630854eef5146102435780630a5b2df1146102715780630d8227da14610286575b600080fd5b61022e610219366004611f9b565b60066020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b610263610251366004611f9b565b60016020526000908152604090205481565b60405190815260200161023a565b61028461027f366004611fc9565b610525565b005b610284610294366004611fc9565b6105b7565b6002546102ac906001600160a01b031681565b6040516001600160a01b03909116815260200161023a565b6102846102d2366004611fc9565b610690565b6102846102e5366004611fc9565b610767565b61022e6102f8366004611f9b565b6000908152600560205260409020546001600160a01b0316151590565b610284610323366004611f9b565b6107d6565b6102ac610336366004611f9b565b6005602052600090815260409020546001600160a01b031681565b6102846108c7565b610284610367366004611fc9565b610906565b61028461037a366004611fed565b610994565b61028461038d36600461202b565b610a21565b60085461022e9060ff1681565b61022e6103ad366004611fc9565b60036020526000908152604090205460ff1681565b61022e6103d0366004611fc9565b60076020526000908152604090205460ff1681565b610284610bd1565b6102846103fb3660046120b1565b610c0f565b61041361040e366004611f9b565b610cb6565b6040805194151585526020850193909352918301521515606082015260800161023a565b610284610445366004611fc9565b610d23565b610284610d74565b610284610460366004611fc9565b610e22565b6000546001600160a01b03166102ac565b6004546102ac906001600160a01b031681565b61028461049736600461212d565b610efc565b6102846104aa366004611fc9565b611079565b61022e6104bd366004611fc9565b60096020526000908152604090205460ff1681565b610263603281565b6102846104e8366004612169565b611108565b610263611768565b610284610503366004611fc9565b61178a565b61026362093a8081565b610284610520366004611fc9565b611818565b61052d611853565b6001600160a01b03811660009081526003602052604090205460ff1661056e5760405162461bcd60e51b8152600401610565906121e8565b60405180910390fd5b6001600160a01b038116600081815260036020526040808220805460ff19169055517f513cdc19277bafa59b350daee3326371868289df4611fab72ab9749643ad7fef9190a250565b6105bf611853565b6001600160a01b0381166105e55760405162461bcd60e51b815260040161056590612215565b610646816040516020016105f9919061223b565b60405160208183030381529060405280519060200120604051806040016040528060198152602001785a4b56657269666965722e6e617374617669546f7661726e6f60381b815250611882565b600480546001600160a01b0319166001600160a01b0383169081179091556040517fe364cf452581b78d64b23d0370f72ba8380777f0a90e52e223986e17f014382790600090a250565b610698611853565b6001600160a01b0381166106be5760405162461bcd60e51b815260040161056590612215565b61071b816040516020016106d29190612274565b60405160208183030381529060405280519060200120604051806040016040528060158152602001742d25ab32b934b334b2b9173237b230b52d32ba37b760591b815250611882565b6001600160a01b038116600081815260036020526040808220805460ff19166001179055517f2e55dec88f978eb8229e3f08822286f2c8befca5884b9de310678253f6d171469190a250565b6004546001600160a01b031633146107b05760405162461bcd60e51b815260206004820152600c60248201526b53616d6f20746f7661726e6160a01b6044820152606401610565565b6001600160a01b03811661071b5760405162461bcd60e51b815260040161056590612215565b6107de611853565b6000818152600560205260409020546001600160a01b03166108335760405162461bcd60e51b815260206004820152600e60248201526d139bdd081c9959da5cdd195c995960921b6044820152606401610565565b60008181526006602052604090205460ff16156108815760405162461bcd60e51b815260206004820152600c60248201526b105b1c9958591e481d5cd95960a21b6044820152606401610565565b60008181526005602052604080822080546001600160a01b03191690555182917f99088e6d10d683bcaedea1a362ff6ebb8ab7d7c38f4e8c852e68161ae4cde7d391a250565b6108cf611853565b6008805460ff1916905560405133907f3a4aad69711d2e0c9ee20d814b95ca020a6a23491162ca2396215a432bbe773990600090a2565b61090e611853565b6001600160a01b0381166109345760405162461bcd60e51b815260040161056590612215565b610991816040516020016109489190612274565b60405160208183030381529060405280519060200120604051806040016040528060158152602001742d25ab32b934b334b2b9173237b230b52d32ba37b760591b8152506119ab565b50565b61099c611853565b6001600160a01b0382166109c25760405162461bcd60e51b815260040161056590612215565b6001600160a01b038216600081815260096020908152604091829020805460ff191685151590811790915591519182527f9562c728fd52688e7dc1a376e3ee2035e842ac8dff05803952c69492f957d88a910160405180910390a25050565b3360009081526007602052604090205460ff16610a715760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610565565b6001600160a01b03811660009081526003602052604090205460ff16610aa95760405162461bcd60e51b8152600401610565906121e8565b60005b82811015610bcb576000848483818110610ac857610ac86122a9565b90506020020135905080600003610b135760405162461bcd60e51b815260206004820152600f60248201526e16995c9bc818dbdb5b5a5d1b595b9d608a1b6044820152606401610565565b6000818152600560205260409020546001600160a01b031615610b6d5760405162461bcd60e51b8152602060048201526012602482015271105b1c9958591e481c9959da5cdd195c995960721b6044820152606401610565565b60008181526005602052604080822080546001600160a01b0319166001600160a01b0387169081179091559051909183917f85819f62de3f36778ef25274607ec7071cf41151e6e04a29971d86f30e8bbf919190a350600101610aac565b50505050565b60405162461bcd60e51b81526020600482015260136024820152722932b737bab731b29037b732b6b7b3b7b1b2b760691b6044820152606401610565565b610c17611853565b6000838152600160205260408120549003610c645760405162461bcd60e51b815260206004820152600d60248201526c4e69206e61706f766564616e6f60981b6044820152606401610565565b600083815260016020526040808220919091555183907f72003eda7c2c39777210bccbf69141a92bbe56d746914a16f5ee3aaf5e6f7a8390610ca990859085906122bf565b60405180910390a2505050565b600081815260016020526040812054819081908190808203610ce657600080600080945094509450945050610d1c565b600181610cf662093a8082612304565b834210158015610d125750610d0e62093a8085612304565b4211155b9450945094509450505b9193509193565b610d2b611853565b6001600160a01b038116600081815260076020526040808220805460ff19169055517fd84b626bc6cd1f6d62d5e197f48869d2a5ef1a750b360ad929c3de63c88bdfe49190a250565b6000546001600160a01b0316331480610d9c57503360009081526009602052604090205460ff165b610de85760405162461bcd60e51b815260206004820152601c60248201527f4e6920706f6f626c617363656e207a61207a61757374617669746576000000006044820152606401610565565b6008805460ff1916600117905560405133907f65b3009f81d789a2064493bfd78059e5eaacca20215307e85f985bf0e513ebd690600090a2565b610e2a611853565b6001600160a01b038116610e505760405162461bcd60e51b815260040161056590612215565b610eb081604051602001610e649190612317565b60405160208183030381529060405280519060200120604051806040016040528060188152602001775a4b56657269666965722e617574686f72697a65554f525360401b815250611882565b6001600160a01b038116600081815260076020526040808220805460ff19166001179055517fb334427bfaed9992168ed99fda266e2d7cf5c2ea1a37a1f76e82fd1e307edf7f9190a250565b3360009081526007602052604090205460ff16610f4c5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610565565b81600003610f8e5760405162461bcd60e51b815260206004820152600f60248201526e16995c9bc818dbdb5b5a5d1b595b9d608a1b6044820152606401610565565b6001600160a01b03811660009081526003602052604090205460ff16610fc65760405162461bcd60e51b8152600401610565906121e8565b6000828152600560205260409020546001600160a01b0316156110205760405162461bcd60e51b8152602060048201526012602482015271105b1c9958591e481c9959da5cdd195c995960721b6044820152606401610565565b60008281526005602052604080822080546001600160a01b0319166001600160a01b0385169081179091559051909184917f85819f62de3f36778ef25274607ec7071cf41151e6e04a29971d86f30e8bbf919190a35050565b611081611853565b6001600160a01b0381166110a75760405162461bcd60e51b815260040161056590612215565b610991816040516020016110bb919061223b565b60405160208183030381529060405280519060200120604051806040016040528060198152602001785a4b56657269666965722e6e617374617669546f7661726e6f60381b8152506119ab565b33816111445760405162461bcd60e51b815260206004820152600b60248201526a16995c9bc8185b5bdd5b9d60aa1b6044820152606401610565565b60085460ff16156111975760405162461bcd60e51b815260206004820152601760248201527f4b6f76616e6a65206a65207a6175737461766c6a656e6f0000000000000000006044820152606401610565565b6001600160a01b03831660009081526003602052604090205460ff166111cf5760405162461bcd60e51b8152600401610565906121e8565b83356000818152600560205260409020546001600160a01b0385811691161461123a5760405162461bcd60e51b815260206004820152601960248201527f436f6d6d69746d656e74206e69207a61207461207a65746f6e000000000000006044820152606401610565565b60008181526006602052604090205460ff16156112905760405162461bcd60e51b8152602060048201526014602482015273105cdcd95d08185b1c9958591e481b5a5b9d195960621b6044820152606401610565565b60208501357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181106113045760405162461bcd60e51b815260206004820152601860248201527f4a61766e69207369676e616c20697a76656e20706f6c6a6100000000000000006044820152606401610565565b6000811161134d5760405162461bcd60e51b81526020600482015260166024820152750446f6b617a616e6120767265646e6f7374206a6520360541b6044820152606401610565565b670de0b6b3a7640000856001600160a01b031663cde2202a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611394573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113b8919061234f565b6113c29190612368565b81111561141f5760405162461bcd60e51b815260206004820152602560248201527f446f6b617a616e6120767265646e6f73742070726573656761207072696a61766044820152646c6a656e6f60d81b6064820152608401610565565b8068056bc75e2d6310000061143560018761238a565b61143f9190612368565b106114975760405162461bcd60e51b815260206004820152602260248201527f4b6f6c6963696e61207072657365676120646f6b617a616e6f20767265646e6f6044820152611cdd60f21b6064820152608401610565565b6114a389898989611a5e565b6114e25760405162461bcd60e51b815260206004820152601060248201526f24b73b30b634b2102d2590383937b7b360811b6044820152606401610565565b6000828152600660209081526040808320805460ff191660011790556005909152812080546001600160a01b03191690556115228a8a8a8a888a8c611f07565b9050600061271061153460328861239d565b61153e9190612368565b90506001600160a01b038716636a3b5c4d8661155a848a61238a565b6040516001600160e01b031960e085901b1681526001600160a01b039092166004830152602482015260448101859052606401600060405180830381600087803b1580156115a757600080fd5b505af11580156115bb573d6000803e3d6000fd5b505050506000876001600160a01b031663469048406040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115ff573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162391906123b4565b604051638cc9d10760e01b81526001600160a01b0380831660048301526024820185905291925090891690638cc9d10790604401600060405180830381600087803b15801561167157600080fd5b505af1158015611685573d6000803e3d6000fd5b50505050806001600160a01b03167fd2b7c9cdd8f0b876462485c766dac30078e9ef6f76ce1e57ca92c7bd553bcf1f836040516116c491815260200190565b60405180910390a26001600160a01b03808916908716867f1365c616f24c2b6838dd9a07b147b6ffd5e232d55f3ffbd59cddbb073def4e92611706868c61238a565b60405190815260200160405180910390a4856001600160a01b03167f0565033f3973b38ac07b3b39f05c0bcfa1449a22d6fd347fdc63c7a8370e106a8460405161175291815260200190565b60405180910390a2505050505050505050505050565b6000610171461461177b57610258611780565b6202a3005b62ffffff16905090565b611792611853565b6001600160a01b0381166117b85760405162461bcd60e51b815260040161056590612215565b610991816040516020016117cc9190612317565b60405160208183030381529060405280519060200120604051806040016040528060188152602001775a4b56657269666965722e617574686f72697a65554f525360401b8152506119ab565b611820611853565b6001600160a01b03811661184a57604051631e4fbdf760e01b815260006004820152602401610565565b61099181611f4b565b6000546001600160a01b031633146118805760405163118cdaa760e01b8152336004820152602401610565565b565b600082815260016020526040812054908190036118d15760405162461bcd60e51b815260206004820152600d60248201526c4e69206e61706f766564616e6f60981b6044820152606401610565565b804210156119175760405162461bcd60e51b815260206004820152601360248201527216985b5a5ac81cd9481b9a481c1bdd195ad95b606a1b6044820152606401610565565b61192462093a8082612304565b4211156119685760405162461bcd60e51b81526020600482015260126024820152714e61706f766564206a6520706f74656b6c6160701b6044820152606401610565565b600083815260016020526040808220919091555183907ffac4537038ebc9eaa00d86a88a6c2e268012d306f6442966aab36014a195432390610ca9908590612417565b600082815260016020526040902054156119f75760405162461bcd60e51b815260206004820152600d60248201526c5a65206e61706f766564616e6f60981b6044820152606401610565565b6000611a01611768565b611a0b9042612304565b60008481526001602052604090208190559050827f63a0d065f46e07e30a4f246d28eb53f839982100439173db92dbd5700ab37c158383611a4f62093a8082612304565b604051610ca99392919061242a565b6000611ecb565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181106109915760008052600080fd5b600060405183815284602082015285604082015260408160608360076107d05a03fa915081611ac75760008052600080fd5b825160408201526020830151606082015260408360808360066107d05a03fa91505080611af75760008052600080fd5b5050505050565b7f0bbf368c7f6fa7f6559bf65da45c33b505a21ff7fb0874c3bd0e2e7baa1b170385527f05c6418bd08aff1241cc2850606a69d8b73a6d9875e25df448f52ae21907c432602086015260006080860186611b9b87357f181032dd16b7eea417d83a9181ab6887bae84d538a8731a6f3003db177c0caba7f083a4c6477d7d1a1feb5f0ba3ddcd764a617b31c182138d6e756100407c25c8984611a95565b611beb60208801357f19f2f485618fdb703d11b2fbc13e61ce7371c4f94e99c148b2d2d8e3f1d9a6547f1a5a3f66f163e8eb3a5351b30773f2cf6126755e91973e02ab7bd68d7752583784611a95565b50823581527f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760208401357f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4703066020820152833560408201526020840135606082015260408401356080820152606084013560a08201527f2d4d9aa7e302d9df41749d5507949d05dbea33fbb16c643b22f599a2be6df2e260c08201527f14bedd503c37ceb061d8ec60209fe345ce89830a19230301f076caff004d192660e08201527f0967032fcbf776d1afc985f88877f182d38480a653f2decaa9794cbc3bf3060c6101008201527f0e187847ad4c798374d0d6732bf501847dd68bc0e071241e0213bc7fc13db7ab6101208201527f304cfbd1e08a704a99f5e847d93f8c3caafddec46b7a0d379da69a4d112346a76101408201527f1739c1b1a457a8c7313123d24d2f9192f896b7c63eea05a9d57f06547ad0cec8610160820152600087015161018082015260206000018701516101a08201527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26101c08201527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6101e08201527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b6102008201527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa610220820152843561024082015260208501356102608201527f10e115799fb9e210b69c6459d325cb70991b1a5f7b23166b9cb1079a84ec05796102808201527f11367b8fccc2b903fc7b4fa13cccc3bd581b6ae5bb74403ff1449df18caeb0616102a08201527f2bc521f6593c958f19d068ab6c9d871471993c59807984ff5540d2ae59f7ad6c6102c08201527f2bf6b0d953792ab8a4e7926e629078462da9a25f808b3d5c13c56b035c14b3f06102e08201526020816103008360086107d05a03fa9051169695505050505050565b6040516103808101604052611ee36000840135611a65565b611ef06020840135611a65565b611efd818486888a611afe565b9695505050505050565b60008787878787878746604051602001611f2898979695949392919061244f565b604051602081830303815290604052805190602001209050979650505050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060208284031215611fad57600080fd5b5035919050565b6001600160a01b038116811461099157600080fd5b600060208284031215611fdb57600080fd5b8135611fe681611fb4565b9392505050565b6000806040838503121561200057600080fd5b823561200b81611fb4565b91506020830135801515811461202057600080fd5b809150509250929050565b60008060006040848603121561204057600080fd5b833567ffffffffffffffff8082111561205857600080fd5b818601915086601f83011261206c57600080fd5b81358181111561207b57600080fd5b8760208260051b850101111561209057600080fd5b602092830195509350508401356120a681611fb4565b809150509250925092565b6000806000604084860312156120c657600080fd5b83359250602084013567ffffffffffffffff808211156120e557600080fd5b818601915086601f8301126120f957600080fd5b81358181111561210857600080fd5b87602082850101111561211a57600080fd5b6020830194508093505050509250925092565b6000806040838503121561214057600080fd5b82359150602083013561202081611fb4565b806040810183101561216357600080fd5b92915050565b600080600080600080610180878903121561218357600080fd5b61218d8888612152565b955060c08701888111156121a057600080fd5b6040880195506121b08982612152565b9450506121c1886101008901612152565b92506101408701356121d281611fb4565b8092505061016087013590509295509295509295565b6020808252601390820152722d32ba37b7103734903837b7b13630b9b1b2b760691b604082015260600190565b6020808252600c908201526b5a65726f206164647265737360a01b604082015260600190565b6040808252600e908201526d6e617374617669546f7661726e6f60901b60608201526001600160a01b0391909116602082015260800190565b6040808252600a90820152693237b230b52d32ba37b760b11b60608201526001600160a01b0391909116602082015260800190565b634e487b7160e01b600052603260045260246000fd5b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115612163576121636122ee565b6040808252600d908201526c617574686f72697a65554f525360981b60608201526001600160a01b0391909116602082015260800190565b60006020828403121561236157600080fd5b5051919050565b60008261238557634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115612163576121636122ee565b8082028115828204841417612163576121636122ee565b6000602082840312156123c657600080fd5b8151611fe681611fb4565b6000815180845260005b818110156123f7576020818501810151868301820152016123db565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000611fe660208301846123d1565b60608152600061243d60608301866123d1565b60208301949094525060400152919050565b6101c081016040808b84378083018a60005b600281101561247e57838284379183019190830190600101612461565b505050808960c08501378088610100850137506001600160a01b03958616610140830152610160820194909452919093166101808201526101a0019190915294935050505056fea26469706673582212203865fd61411c648c2f358d2522bed2471f1d9664536a68862fc5374b8e98db1864736f6c63430008180033