Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

How to Implement Geographic Restrictions in Your Sale

A developer-focused guide on implementing geographic restrictions for token sales. Covers IP-based frontend blocking, on-chain wallet screening services, and decentralized identity solutions with code examples and compliance considerations.
Chainscore © 2026
introduction
COMPLIANCE

How to Implement Geographic Restrictions in Your Sale

A technical guide for developers on implementing location-based access controls for token sales and NFT mints to comply with regulatory requirements.

Geographic restrictions, or geo-blocking, are a critical compliance tool for Web3 projects launching public sales. They allow you to programmatically restrict access to your smart contract's minting or purchase functions based on a user's perceived location. This is primarily used to block participants from jurisdictions with stringent securities regulations (like the United States) or comprehensive sanctions lists. Implementing these checks on-chain, rather than just on a frontend, is essential for creating a verifiable and enforceable compliance record. The core mechanism involves integrating a decentralized oracle service, such as Chainlink Functions or the API3 QRNG, to fetch and verify a user's IP-derived country code during a transaction.

The standard implementation pattern involves a modifier or a function that queries an oracle before allowing a sale to proceed. For example, you might store a list of restricted country codes (like ["US", "CA", "KP"]) in your contract. When a user calls mint(), the contract first calls an external oracle to get the user's country code. This request is typically signed by the transaction's msg.sender to prove the caller's origin. If the returned country code is on the blocklist, the transaction reverts. It's crucial to understand that this check is based on IP address geolocation, which can be circumvented by sophisticated users with VPNs, but it establishes a necessary compliance baseline.

Here is a simplified conceptual example using a modifier pattern, assuming an oracle interface:

solidity
modifier notRestricted() {
    string memory countryCode = GEO_ORACLE.getCountryCode(msg.sender);
    require(!isCountryRestricted(countryCode), "Sale not available in your region");
    _;
}

function mint() external payable notRestricted {
    // Minting logic here
}

You must carefully manage the oracle response latency and cost. On-chain oracle calls consume gas and may require the user to pay for the request. Services like Chainlink Functions handle this by allowing the developer to pre-fund a subscription, abstracting the gas cost from the end-user. Always implement a fail-open or fail-closed mechanism; decide if the transaction should proceed or fail if the oracle call times out or reverts, based on your risk tolerance.

Beyond the core blocking logic, consider these implementation details for a robust system. Maintain an upgradable blocklist controlled by a multisig or DAO vote to adapt to changing regulations. Emit clear events like CountryChecked(address user, string countryCode, bool allowed) for transparency and audit trails. For NFT projects, consider storing the proof of the geo-check (like a timestamped oracle request ID) as metadata associated with the minted token. This creates an immutable compliance record. Remember, geographic restrictions are one layer of a compliance strategy and should be combined with other measures like KYC/AML providers (e.g., Coinbase Verifications or Parallel Markets) for higher-stakes offerings.

When selecting an oracle solution, evaluate providers based on data freshness, decentralization, cost, and reliability. Chainlink Functions provides a generalized framework to call any API, while API3's QRNG offers a verifiable random number that can be used as a seed for decentralized attestation services. Test your implementation thoroughly on a testnet, simulating requests from different virtual locations. Ultimately, well-implemented geographic restrictions protect your project from regulatory missteps while maintaining the decentralized, permissionless ethos where legally permissible. Always consult with legal counsel to determine the specific jurisdictions you must restrict.

prerequisites
PREREQUISITES AND LEGAL CONSIDERATIONS

How to Implement Geographic Restrictions in Your Sale

A technical guide to implementing compliant geographic restrictions for token sales and NFT mints using smart contracts and off-chain verification.

Implementing geographic restrictions is a critical step for projects that must comply with international sanctions and securities regulations, such as those from the Office of Foreign Assets Control (OFAC) or the Securities and Exchange Commission (SEC). The primary goal is to programmatically prevent users from prohibited jurisdictions from participating in your sale. This involves a two-pronged approach: off-chain verification to screen participants during a whitelist or KYC process, and on-chain enforcement to block restricted addresses from interacting with your smart contract. Relying solely on a terms-of-service clickwrap is insufficient for regulatory compliance.

The most robust method for on-chain restriction is integrating a blockchain oracle that provides real-time geolocation or sanctions data. Services like Chainlink Functions or API3 can fetch verified data from a compliance provider's API and deliver it to your smart contract. Your mint function would then include a check, reverting the transaction if the user's IP-derived country code matches a blocked list. For example, your mint function might require a successful call to an oracle contract: require(geoCompliance.check(msg.sender) == true, "Restricted jurisdiction");. This creates a verifiable and tamper-resistant record of compliance on-chain.

For projects without oracle integration, a simpler blocklist approach can be implemented directly in the contract. After completing an off-chain KYC process, approved addresses are added to an on-chain whitelist. The contract's mint function then checks require(whitelist[msg.sender], "Not authorized");. Crucially, addresses from restricted regions are never added to this list. While this method centralizes the screening process off-chain, the on-chain enforcement is clear and immutable. It is essential to use a secure, permissioned function (e.g., onlyOwner) to manage the whitelist to prevent unauthorized additions.

Always complement technical restrictions with clear legal disclaimers in your user interface and smart contract code. Your website should display a list of excluded countries, and users should acknowledge this before connecting their wallet. Consider adding a comment in your contract's Solidity code, such as /// @notice This sale is not available to residents of embargoed jurisdictions.. Furthermore, maintain logs of your off-chain verification process. In the event of a regulatory inquiry, you must demonstrate a good-faith effort to screen participants, which includes documenting the tools and data sources used for geographic checks.

Finally, test your restrictions thoroughly in a testnet environment before deployment. Use wallet addresses simulated from different regions to ensure your oracle calls or whitelist logic correctly pass or revert transactions. Remember that regulations change; design your system with upgradability in mind, using a proxy pattern or a mutable contract owner who can update the list of restricted regions in your oracle feed or whitelist manager. Non-compliance can result in severe penalties, so treating geographic restrictions as a core, auditable component of your smart contract system is not optional.

method-1-ip-blocking
GEO-RESTRICTION TUTORIAL

Method 1: IP-Based Frontend Blocking

Implement a client-side check to restrict access to your token sale or application based on a user's IP address and geographic location.

IP-based frontend blocking is a client-side technique that prevents users from specific countries or regions from accessing your web application. This method uses a user's public IP address to determine their approximate geographic location. When a user loads your site, your frontend code makes an API call to a geolocation service. If the returned country code matches a restricted region, the application can display a blocking message or redirect the user. This approach is implemented entirely in the user's browser using JavaScript, making it quick to deploy but fundamentally reliant on the client's honesty and the accuracy of the geolocation service.

The core of this method involves integrating a geolocation API. Services like ipapi.co, ipgeolocation.io, or Abstract API offer free tiers for basic lookups. Your frontend fetches the user's IP data, typically returning an object containing the country_code (e.g., 'US', 'CN'). You then compare this code against a denylist you maintain. A critical limitation is that this check happens after the page loads. A technically savvy user can bypass it by disabling JavaScript, using a VPN, or inspecting and modifying the network request. Therefore, this method should be considered a user experience layer, not a security measure.

Here is a basic implementation example using the Fetch API and ipapi.co. This code runs when your application's main component mounts, checks the country, and updates the application state accordingly.

javascript
// Example React component logic
const [isBlocked, setIsBlocked] = useState(false);

useEffect(() => {
  const checkGeoRestriction = async () => {
    try {
      const response = await fetch('https://ipapi.co/json/');
      const data = await response.json();
      const restrictedCountries = ['US', 'CA']; // Your denylist
      
      if (restrictedCountries.includes(data.country_code)) {
        setIsBlocked(true); // Block access
      }
    } catch (error) {
      console.error('Geolocation fetch failed:', error);
      // Decide on fail-open or fail-closed policy
    }
  };
  
  checkGeoRestriction();
}, []);

// In your JSX
return (
  <div>
    {isBlocked ? (
      <div>Access is not available in your region.</div>
    ) : (
      <YourMainApplication />
    )}
  </div>
);

For production use, consider these optimizations and caveats. First, implement the check as early as possible, perhaps in a root layout or a dedicated middleware script, to prevent flashing of blocked content. Second, cache the geolocation result in localStorage to avoid excessive API calls on subsequent page loads, but include a mechanism to refresh it periodically. Third, always have a fallback plan for when the API call fails—will you allow or deny access? A 'fail-open' policy (allow access) is more user-friendly but less restrictive. Remember, determined users can spoof their IP via VPNs, so this method is insufficient for enforcing strict legal compliance; it primarily serves as a clear notice to users.

This frontend method is best suited for scenarios where you need to communicate regional unavailability quickly or for products where the consequence of a bypass is low. It is a common first step in a defense-in-depth strategy for geographic compliance. For enforceable restrictions, especially in regulated token sales, you must combine this with server-side validation and potentially smart contract-level checks that validate user eligibility on-chain before allowing transactions. The frontend block acts as the initial, visible gate, while backend systems provide the enforceable barrier.

method-2-wallet-screening
GEOGRAPHIC COMPLIANCE

Method 2: On-Chain Wallet Screening

Implement programmatic restrictions by verifying wallet geography directly on-chain, ensuring your token sale adheres to regional regulations.

On-chain wallet screening enforces geographic restrictions by checking a wallet's provenance before allowing it to interact with your sale contract. This method is more robust than frontend-only checks, as it prevents users from bypassing restrictions by interacting directly with the contract via tools like Etherscan. The core mechanism involves integrating a decentralized oracle or a permissioned signer service that can attest to a user's geographic eligibility based on their wallet's transaction history or a verified attestation. Services like Chainlink Functions or custom oracle networks can fetch verified off-chain data, such as IP geolocation results from a trusted provider, and deliver them on-chain for your smart contract to evaluate.

A typical implementation involves a two-step process. First, a user requests eligibility by signing a message from their wallet, which is sent to your off-chain verification service. This service performs the geographic check (e.g., against a list of sanctioned jurisdictions) and, if approved, returns a cryptographically signed permission ticket. Second, the user submits this signed ticket when calling the purchase function in your sale contract. The contract verifies the signature against a known public key before processing the transaction. This pattern, using off-chain verification with on-chain permission checks, keeps gas costs low while maintaining enforceable compliance.

Here is a simplified Solidity example of the contract-side verification logic. The contract stores the public address of the trusted signer and checks for a valid EIP-712 signature attesting the buyer's eligibility.

solidity
function purchaseTokens(uint256 amount, bytes calldata signature) external payable {
    // Recreate the message that was signed off-chain
    bytes32 messageHash = keccak256(abi.encodePacked(msg.sender, "APPROVED"));
    bytes32 ethSignedMessageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash));
    
    // Recover the signer from the signature
    address recoveredSigner = recoverSigner(ethSignedMessageHash, signature);
    
    // Verify the signer is the trusted oracle
    require(recoveredSigner == trustedSigner, "Invalid or forged permission");
    
    // Proceed with the token purchase logic
    _processPurchase(msg.sender, amount);
}

This ensures only wallets that have received a valid, recent attestation from your compliance oracle can successfully call the function.

Key considerations for this method include oracle reliability and decentralization. Relying on a single centralized oracle creates a point of failure and potential censorship. For higher assurance, consider using a decentralized oracle network that requires multiple node consensus. You must also manage the freshness of attestations to prevent replay attacks; including a nonce or block timestamp in the signed message is crucial. Furthermore, be aware of the privacy implications, as linking wallet addresses to geographic data must be handled in compliance with data protection laws like GDPR. The design should minimize data stored on-chain, keeping sensitive verification details off-chain.

For production use, established infrastructure providers offer streamlined solutions. Chainlink Functions allows your contract to request an HTTP call to a trusted API (like an IP geolocation service) and receive the result in the same transaction. Alternatively, platforms like Gitcoin Passport or Verax provide frameworks for issuing and verifying on-chain attestations regarding a user's credentials, which can include geographic compliance. Integrating these tools shifts the complexity of compliance logic and data sourcing to specialized, audited protocols, reducing your development overhead and security risk while ensuring your sale remains accessible only to users from permitted regions.

method-3-decentralized-identity
METHOD 3: DECENTRALIZED IDENTITY (DID) AND PROOF-OF-PERSONHOOD

How to Implement Geographic Restrictions in Your Sale

Use decentralized identity and proof-of-personhood protocols to verify user eligibility based on jurisdiction, enabling compliant sales without collecting sensitive personal data.

Decentralized Identity (DID) and proof-of-personhood protocols offer a privacy-preserving method for implementing geographic restrictions. Instead of requiring users to submit government IDs or physical addresses, these systems allow individuals to generate verifiable credentials that prove specific claims—such as residency in a permitted jurisdiction—without revealing the underlying personal data. Protocols like Worldcoin's World ID or Civic's Verified Person use zero-knowledge proofs (ZKPs) to allow a user to cryptographically prove they are a unique human from an allowed country, while the sale contract only receives a true/false verification signal. This aligns with data minimization principles and reduces regulatory risk associated with storing personal information.

To implement this, you first integrate a verification provider into your dApp's frontend. When a user connects their wallet, you prompt them to verify their personhood and geographic eligibility using the chosen protocol (e.g., by scanning an orb for World ID or completing a Civic check). The protocol's smart contract or API returns a verifiable credential or a proof. Your sale contract must then include a modifier or require statement that checks for a valid proof from a trusted issuer. For example, you could store a mapping of verified DIDs to eligibility status or verify a ZK-SNARK proof on-chain.

Here is a simplified Solidity example using a signature-based verification pattern, assuming an off-chain service attests to a user's eligibility and signs a message. The contract checks the signature against a known verifier address.

solidity
function purchaseTokens(bytes calldata signature) external payable {
    bytes32 messageHash = keccak256(abi.encodePacked(msg.sender, "PERMITTED_JURISDICTION"));
    bytes32 ethSignedMessageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash));
    address signer = ecrecover(ethSignedMessageHash, signature);
    require(signer == TRUSTED_VERIFIER_ADDRESS, "Invalid or unverified proof");
    // ... proceed with sale logic
}

This approach requires running or relying on a trusted off-chain verification service that complies with jurisdictional rules.

Key considerations for this method include issuer trust—you must rely on the identity protocol's governance and accuracy—and user accessibility, as not all users may have or want to use the required identity solution. Furthermore, the legal standing of these digital proofs varies by jurisdiction; consult legal counsel to ensure compliance. This method is best suited for sales targeting a broad, permissioned audience where user privacy is a priority and where you can integrate with a robust, widely-adopted proof-of-personhood network. Always audit the security of the verification flow to prevent spoofing or replay attacks.

IMPLEMENTATION APPROACHES

Comparison of Restriction Methods

A technical comparison of on-chain and off-chain methods for enforcing geographic restrictions in token sales.

Feature / MetricOn-Chain AllowlistOff-Chain VerificationHybrid (IP + On-Chain)

Implementation Complexity

High

Low

Medium

Gas Cost per Verification

$2-5

$0.1-0.5

$1-3

Real-time Blocking

Resistance to VPN/Proxy

User Privacy Exposure

Wallet address only

IP address, wallet

IP hash, wallet

Compliance Audit Trail

Integration with Standard Sale Contracts

Requires custom logic

Frontend-only possible

Requires custom logic

Maintenance Overhead

High (list updates)

Low (service managed)

Medium

implementation-walkthrough
GEOGRAPHIC RESTRICTIONS

Implementation Walkthrough: Smart Contract Integration

A step-by-step guide to implementing compliant geographic restrictions directly within your token sale or NFT minting smart contract.

Geographic restrictions, often required for regulatory compliance, prevent users from specific countries or regions from participating in a token sale. Implementing these checks directly in your smart contract's mint or buyTokens function is the most secure and transparent method. This approach ensures that restrictions are enforced on-chain, are immutable once deployed, and are verifiable by any user. We'll implement this using a simple, gas-efficient pattern that checks a user's eligibility based on a pre-configured list of restricted country codes before allowing a transaction to proceed.

The core logic involves storing a mapping of restricted country codes and integrating an oracle or trusted data source to provide a user's country code during the transaction. For this example, we'll use a simplified on-chain mapping that the contract owner can manage. The key function isRestrictedCountry will be called within your primary sale function. We assume an external service (like Chainlink Oracles or a custom API) has appended the user's two-letter ISO country code (e.g., 'US', 'CN') to the transaction call data, which we pass as a parameter.

Here is a basic Solidity implementation outline for an ERC721 minting contract with geographic checks:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract GeoRestrictedMint {
    address public owner;
    mapping(string => bool) public isCountryRestricted;

    constructor() {
        owner = msg.sender;
        // Initialize restricted countries
        isCountryRestricted["US"] = true;
        isCountryRestricted["CN"] = true;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    function mint(string memory countryCode) external payable {
        require(!isCountryRestricted[countryCode], "Country restricted");
        // ... proceed with minting logic
    }

    function updateRestriction(string memory countryCode, bool restricted) external onlyOwner {
        isCountryRestricted[countryCode] = restricted;
    }
}

In this snippet, the mint function reverts if the provided countryCode exists in the isCountryRestricted mapping. The owner can update the list via updateRestriction.

For production use, you must integrate a reliable method to obtain the user's country code. Manually passing it as a parameter is insecure, as users can falsify it. The recommended approach is to use a decentralized oracle network like Chainlink Functions or a similar verifiable randomness/API call service. The oracle would fetch the user's IP-geolocation data off-chain, verify it, and deliver the country code on-chain in the same transaction via a callback. Your mint function would then be permissioned, only callable by the oracle's pre-defined callback address.

Consider gas costs and user experience. On-chain storage of country codes is cheap for a modest list, but frequent updates by the owner cost gas. For dynamic lists, consider storing a Merkle root of the restricted countries on-chain and having users provide a Merkle proof of non-membership. This advanced technique, used by protocols like Uniswap for allowlists, keeps gas costs low for users and allows for efficient batch updates by the admin. Always thoroughly test restriction logic on a testnet with tools like Hardhat or Foundry to simulate users from different regions.

Finally, remember that smart contract restrictions are just one layer. You should also implement off-chain checks at the frontend level using IP geolocation services to improve user experience by preventing failed transactions. However, the on-chain check remains the ultimate enforcement layer. Always disclose the use of geographic restrictions clearly to users and consult with legal counsel to ensure your implementation meets the specific regulatory requirements of your project.

limitations-risks
TECHNICAL LIMITATIONS AND RISKS

How to Implement Geographic Restrictions in Your Sale

Implementing geographic restrictions, or geo-blocking, is a common requirement for token sales to comply with local regulations. This guide covers the technical approaches, limitations, and inherent risks of enforcing these restrictions on-chain.

Geographic restrictions are typically enforced by checking a user's IP address or wallet address against a blocklist. The most common on-chain method uses an allowlist or blocklist of wallet addresses, which is set by the contract owner before the sale. This is a binary check: if a user's address is on the blocked list, the smart contract's require statement will revert their transaction. However, this method has a critical limitation: it only restricts wallets known before the sale. It cannot prevent a restricted user from creating a new, unrestricted wallet and using it to participate, which is trivial to do.

A more dynamic approach involves using oracles to verify user location in real-time. Services like Chainlink Functions or API3 can fetch a user's approximate geographic data based on their IP address when they interact with your contract. The contract sends an API request, and the oracle returns a response (e.g., a country code) that your contract logic uses to allow or deny the transaction. While more robust, this method introduces complexity, cost (for oracle calls), and a reliance on external data providers. It also cannot guarantee absolute accuracy, as users may employ VPNs or proxies to mask their location.

The fundamental technical risk of any geo-blocking solution is that blockchain transactions are pseudonymous. There is no reliable, decentralized way to link a wallet address to a real-world identity or physical location. Oracle-based solutions depend on centralized data, creating a trust assumption and a potential single point of failure. Furthermore, regulatory bodies may not view IP-based blocking as sufficient compliance, as it is easily circumvented. Developers must clearly communicate these limitations to their project's legal team and understand that on-chain restrictions are a technical barrier, not a legal guarantee.

When implementing, your smart contract function should include a modifier or require statement. For a simple blocklist, it looks like this:

solidity
mapping(address => bool) public isBlocked;

modifier notBlocked(address _user) {
    require(!isBlocked[_user], "Address is restricted");
    _;
}

function participate() external payable notBlocked(msg.sender) {
    // Sale logic
}

For an oracle-based check, the logic would be asynchronous, requiring the user to initiate a request and then call a second function after the oracle responds, which adds UX friction.

Best practices involve using a layered approach. Combine an on-chain blocklist for known high-risk addresses with an initial off-chain KYC (Know Your Customer) process that collects and verifies identity documents before adding a user to an allowlist. The final sale contract then only mints tokens to pre-approved addresses. This hybrid model shifts the complex legal verification off-chain while using the blockchain for transparent, immutable execution of the approved sale terms. Always conduct a thorough legal review to ensure your chosen method meets the specific regulatory requirements of the jurisdictions you are excluding.

tools-resources
GEO-RESTRICTED SALES

Tools and Resources

Implementing geographic restrictions requires a multi-layered approach, from on-chain verification to legal compliance. These tools and concepts are essential for developers building compliant token sales.

03

Smart Contract Allowlists with Proof

Implement an on-chain allowlist (whitelist) that requires users to submit cryptographic proof of eligibility. A common pattern uses a signed message from a trusted verifier backend that checks KYC/geolocation off-chain. The contract stores a mapping of verified addresses and a signature, allowing only pre-approved users to mint or purchase. This keeps the verification logic off-chain for flexibility while enforcing rules on-chain.

Key functions include:

  • verifySignature(address user, bytes signature)
  • mintVerified(address to, uint256 amount)
GEOGRAPHIC RESTRICTIONS

Frequently Asked Questions

Common questions and solutions for developers implementing location-based access controls in token sales and smart contracts.

Geographic restrictions are programmatic rules that block or allow access to a smart contract function—like a token sale—based on a user's perceived location. They are primarily used for regulatory compliance, ensuring a project does not offer securities or financial services to residents of jurisdictions where it is not licensed (e.g., the United States, China).

From a technical perspective, these are access control modifiers that check an off-chain data source (like an IP geolocation API) before permitting an on-chain transaction. The core challenge is bridging the off-chain verification with on-chain execution in a secure and reliable way.

conclusion
IMPLEMENTATION GUIDE

Conclusion and Best Practices

Successfully implementing geographic restrictions requires careful planning, testing, and maintenance. This section consolidates key takeaways and operational best practices.

Implementing geographic restrictions is a technical and compliance-driven process. Start by clearly defining your legal requirements and target jurisdictions. Use a reliable, low-latency IP geolocation service like MaxMind GeoIP2 or IPinfo for accurate country-level data. Always implement these checks server-side to prevent client-side manipulation. For on-chain sales, integrate the restriction logic directly into your mint or purchase function's require statements. For off-chain sales, validate the user's IP address before processing any payment or generating a signature.

Testing is critical before mainnet deployment. Use VPNs or proxy services to simulate access from blocked and allowed countries. Test edge cases, such as users with IPv6 addresses or those accessing your site through TOR. For smart contracts, deploy and thoroughly test your sale contract on a testnet like Sepolia or Goerli. Consider implementing a pause mechanism or an upgradable proxy pattern so you can update the allowed country list or fix issues without redeploying the entire contract. Document your restriction logic and the legal basis for each blocked region for internal compliance.

Maintain and monitor your implementation post-launch. IP geolocation databases are updated frequently; ensure your service's data is refreshed regularly. Set up alerts for failed transactions or access attempts that trigger the restriction to identify potential issues or attack vectors. Be transparent with your users: clearly state the geographic restrictions in your Terms of Service and display a user-friendly error message (e.g., "Service not available in your region") instead of a generic transaction failure. This approach balances regulatory adherence with a professional user experience.

How to Implement Geographic Restrictions in Your Token Sale | ChainScore Guides