Authorizing access list purchases

The contracts use a process of approving an access list purchase called ECR Recovery. Put simply, the contract allows wallets that are access listed to buy tokens only if the authorising wallet has pre-approved the address. This allows you to implement your own access listing logic.

When configuring a contract, you will be asked to provide an Access List Authorising Address. This should only be used to approve access list purchases and should not receive any funds.

The following code can be used on your server to authorise a wallet for an access list purchase:

// Ethers documentation: https://docs.ethers.io/v5/getting-started/
import { ethers } from 'ethers';
import dayjs from 'dayjs';

// address: The wallet address of the customer to approve
async function approveAuthorised(
    address: string, // User address
    amount: number, // Amount of tokens user wants to buy
): Promise<{
    amount: number;
    totalPrice: number;
    expires: number;
    signature: string;
}> {
    const privateKey = '<<access listA authorising address private key>>';
    const contractAddress = '<<contract address from UI or API>>';

    // Initialise a wallet using the private key
    const wallet = new ethers.Wallet(privateKey);

    // Convert price in eth into gwei
    const price = ethers.utils.parseEther(ethPrice.toString());
    
    // The contract accepts the total price, not a price per token
    const totalPrice = price.mul(amount).toNumber();

    // Expiry timestamp (must be in unix time) of the signature being generated
    const expires = dayjs().add(15, 'minute').unix();

    // Hash together the following:
    // Contract address, user's address, token amount, token price, expiry
    const solidityHash = ethers.utils.solidityKeccak256(
        ['address', 'address', 'uint256', 'uint256', 'uint256'],
        [contractAddress, address, amount, totalPrice, expires]
    );

    // Generate the signature
    const signature = await wallet.signMessage(
        ethers.utils.arrayify(solidityHash)
    );

    return {
        amount,
        totalPrice,
        expires,
        signature
    };
}

The amount, totalPrice, expires, signature values returned can be safely sent to the client to be used in the Client SDK buyAuthorised function or by calling the contract directly by another means.

Securing Private Keys

HyperMint uses 2 separate wallets:

  • Customer Wallet: Receives funds from primary sales
  • Access List Authorising Wallet: Approves access list purchases

To access the wallet, a private key is used which depending on the wallet, allows the user access to transfer funds, approve an access list purchase and any other possible action on a wallet.

🚧

It is essential that you keep your private key safe. Do not commit it in code to a repository

The access listing process requires access to a wallet to sign a message which in turn requires a private key or similar mechanism. There are various ways to keep this key safe going from the most basic to more secure:

  • Use environment variables on the server
  • Use a secrets manager such as AWS Secret Manager
  • Use a key management solution such as AWS KMS

The first two options can use the above code to initialise a wallet with the key while the last option requires a different approach.

AWS KMS

📘

Although AWS is called out here, other key management solutions exist. HyperMint does not endorse or recommend any individual solution

If you are using AWS for your hosting, it may make sense to also use Key Management Service (KMS) to manage the private keys of your wallet. https://aws.amazon.com/kms/

At a high level, the private key can never be extracted from the service and therefore cannot be shared or lost, protecting the wallet. Instead, you send transactions and messages to be signed to the service and get back a signature to use.

Many guides exist on Google including this one: https://luhenning.medium.com/the-dark-side-of-the-elliptic-curve-signing-ethereum-transactions-with-aws-kms-in-javascript-83610d9a6f81

A NPM module also exists to simplify the process: https://www.npmjs.com/package/ethers-aws-kms-signer

❗️

Always review third party code and modules before using in your own systems

Feedback

Something not quite right, unclear or can't find what you are looking for? Please let us know at support.moonpay.com and we will get back to you as soon as we can.