Back to Blog

Secure Smart Contract Development (2) — How to Use Digital Signature and Use It Right in NFT (Markets)

August 12, 2022

1. Introduction

Digital signature is used to ensure the authenticity and integrity. As described in this article, “A valid digital signature, where the prerequisites are satisfied, gives a recipient very high confidence that the message was created by a known sender (authenticity), and that the message was not altered in transit (integrity).”

Digital signature has been widely used in smart contracts, e.g., in allowlist mint and order-book NFT marketplaces. That’s because it helps save transaction costs (off-chain sign and on-chain verification). However, the misuse of the developers also introduces risks in the NFT marketplaces. In this blog, we’d like to talk about the misuse of digital signatures in the NFT ecosystem.

2. Applications

Digital signature has been widely used for allowlist mint (only the users with valid signatures can mint NFTs) in NFT contracts and NFT markets for order verification (only the orders with expected signatures can be executed.) The sign of the data is off-chain to save gas. In the following, we will illustrate these two usage scenarios.

2.1. Allowlist Mint

“NFT minting” is the procedure of creating an NFT on the blockchain. Most NFT projects would like to disseminate their products; they prefer to motivate users by allowlist mint (also called presale, etc.). People who win the spots could mint tokens at a lower price (even free). A digital signature is used to distinguish the allowlist minters and public (ordinary) minters. Below is an example of the implementation of allowlist mint.

function mint_approved(
        vData memory info,
        uint256 number_of_items_requested,
        uint16 _batchNumber
    ) external {
        ...
        require(verify(info), "Unauthorised access secret");
        ...
    }
    function verify(vData memory info) public view returns (bool) {
        require(info.from != address(0), "INVALID_SIGNER");
        bytes memory cat =
            abi.encode(
                info.from,
                info.start,
                info.end,
                info.eth_price,
                info.dust_price,
                info.max_mint,
                info.mint_free
            );
        bytes32 hash = keccak256(cat);
        require(info.signature.length == 65, "Invalid signature length");
        bytes32 sigR;
        bytes32 sigS;
        uint8 sigV;
        bytes memory signature = info.signature;        assembly {
            sigR := mload(add(signature, 0x20))
            sigS := mload(add(signature, 0x40))
            sigV := byte(0, mload(add(signature, 0x60)))
        }        bytes32 data =
            keccak256(
                abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)
            );
        address recovered = ecrecover(data, sigV, sigR, sigS);
        return signer == recovered;
    }

This code snippet is from the Association NFT (Which has a vulnerability — do not copy this code). The function mint_approved() intends to implement the allowlist mint: the project owner signs a mint message (info variable) and sends the message to the permitted minter (who can mint NFTs). Then the minter can invoke approved_mint with the signed variable. The contract will then verify whether the message is signed by the project (signer == recovered). If so, the one who invokes the function is allowed to mint NFTs (which is NOT secure since there is no verification on whether the one who invokes the function is the actual person in the allowlist).

2.2. Order Verification

Order verification is another application of digital signature in the NFT ecosystem. The NFT marketplaces play an essential role in the NFT ecosystem as they provide the trading functionality for the NFTs. As each NFT token is non-fungible, the automated market maker (AMM) trade policy is hard to be used in NFT markets. Thus, most NFT marketplaces, e.g. OpenSea, LooksRare, and X2Y2, take the order-book trade model.

The order-book trade is simple. There is a maker, aka a person who wants to sell an asset for a specific price, and a taker, aka a person who wants to buy the asset at the seller’s price. In this case, the order matches. The process is the same in the order-book NFT marketplaces. The only difference is the process of order offering: the NFT marketplaces use digital signatures for order verification. Figure 1 describes an example of the whole trade process of one of the order-book marketplaces: OpenSea.

**Fig 1. OpenSea trading Process**
Fig 1. OpenSea trading Process

Specifically, the seller signs a sell order and stores it on OpenSea’s server. The buyer could retrieve the signed sell order information from OpenSea’s server and then invokes the NFT market contract with the signed sell order as parameters. The market contract will validate the order to ensure that the seller signs the sell order (since the buyer initiates the transaction.) — to prevent the buyer buys an asset without the seller’s consent.

3. Security Incidents

The Horton Principle is a maxim for cryptographic systems and can be expressed as “Authenticate what is being meant, not what is being said” or “mean what you sign and sign what you mean”, it requires signing the action totally and precisely. If the signature is partially or non-accurate, the result will be disastrous.

3.1 Association NFT

Recalling the NBA NFT contract in section 2.1. The function verify does a standard signature verification, but it's missing one CRITICAL component. The signature verification only ensures that the message is signed by the project. However, there is no enforcement that the person who provides the signature to the contract is consistent with the allowlist minter in the signed message. As a result, anyone can use the same signature to pass the verification and mint NFTs.

3.2 OpenSea

Another security issue is about OpenSea. In early 2022, researchers disclosed a potential vulnerability of the OpenSea marketplace contract (version: wyvern 2.2), which implements the core functionality of NFTs trading.

In Wyvern protocol, users author listings (sell offers) or offers (buy offers) off-chain, and signatures of offers are verified on-chain. Wyvern offers contain many parameters and the parameters are aggregated together into a single bytes string to calculate the digest of the offer. Then the contract will validate the signature of the digest. The parameters aggregation method simply packs the parameters into a bytes string with the following methods.

index = ArrayUtils.unsafeWriteAddress(index, order.target);
index = ArrayUtils.unsafeWriteUint8(index, uint8(order.howToCall));
index = ArrayUtils.unsafeWriteBytes(index, order.calldata);
index = ArrayUtils.unsafeWriteBytes(index, order.replacementPattern);
index = ArrayUtils.unsafeWriteAddress(index, order.staticTarget);
index = ArrayUtils.unsafeWriteBytes(index, order.staticExtradata);
index = ArrayUtils.unsafeWriteAddress(index, order.paymentToken);

For example, if the parameters compose of 2 components: (address, bytes), and the parameters are (0x9a534628b4062e123ce7ee2222ec20b86e16ca8f, "0xc098"), the aggregated bytes would be 0x0000000000000000000000009a534628b4062e123ce7ee2222ec20b86e16ca8fc098, just address + bytes. Seems to be easy and clear, right?

Now, consider a more complex example, the structure of parameters is (address, bytes, bytes).

parameter 1 is _(0x9a534628b4062e123ce7ee2222ec20b86e16ca8f, "0xab", "0xcdef")_.

parameter 2 is _(0x9a534628b4062e123ce7ee2222ec20b86e16ca8f, "0xabcd", "0xef")_.

The aggregated bytes are:

parameter 1: _0x0000000000000000000000009a534628b4062e123ce7ee2222ec20b86e16ca8fabcdef_.

parameter 2: _0x0000000000000000000000009a534628b4062e123ce7ee2222ec20b86e16ca8fabcdef_.

Wow! two different parameters have the same aggregated result, which means their digests are SAME, resulting that one signature could verify the two different parameters.

This is because there are many variable-length components in the parameters. An attacker can truncate part of the variables and attach the truncated parts to their previous or later components. Unfortunately, Wyvern contracts have many variable-length parameters as the below shows.

......
    address target;
    /* HowToCall. */
    AuthenticatedProxy.HowToCall howToCall;
    /* Calldata. */
    bytes calldata;
    /* Calldata replacement pattern, or an empty byte array for no replacement. */
    bytes replacementPattern;
    /* Static call target, zero-address for no static call. */
    address staticTarget;
    /* Static call extra data. */
    bytes staticExtradata;
    ......

The impact of the vulnerability is that attacker could (if possible) control the victim’s accounts to execute some malicious behaviours. A detailed analysis of the vulnerability is here.

Both two security incidents mentioned in this section all violate the Horton Principle. Specifically, the NBA contract does not include the minter in the signed message (or does not check the consistency with the information contained in the signed message with the actual invocator), and the Wyvern contract signs structureless parameters so that the meaning of the action could be modified while the presentation (saying) of the parameters remains.

4. Suggestions

Follow the Horton Principle, sign what you mean, not what you say. The signature should contain all-around and accurate information needed.

  • Put all information that to be verified in the signature. Check the consistency of the data in the signed message with the runtime value (e.g., the intended user in the signed message and the actual user.)
  • The message to be signed needs to be deterministically encoded, e.g., there do not exist messages that are with different structures but have the same encoding result.

Read the Other Article in This Series

Sign up for the latest updates
~$1.6M Lost: Moke Token, LpdFi Exploits | BlockSec Weekly
Security Insights

~$1.6M Lost: Moke Token, LpdFi Exploits | BlockSec Weekly

During the week of August 3-9, 2026, 2 notable security incidents on BNB Chain resulted in approximately $1.6M in total losses, both from price manipulation. The highlighted LpdFi incident (~$697K) reused the same manipulable PancakeSwap pair reserves for both order valuation and interest redemption, letting the attacker inflate a position's principal and reshape the pool to redeem an oversized interest claim. Moke Token (~$906K) combined a manipulable spot price with duplicated LP dividend accounting to claim inflated MOKE and collect the resulting BNB dividends multiple times.

COLDCARD Incident: When a Wallet's "Random" Seed Wasn't Random
Security Insights

COLDCARD Incident: When a Wallet's "Random" Seed Wasn't Random

A silent build-and-integration bug in COLDCARD firmware routed Bitcoin seed generation onto a software RNG fallback, whose weak randomness left wallet seeds recoverable offline. Because the weakness is in the seed itself, a firmware update cannot undo it; verified sweeps reached 1,405 BTC (~$91M) by 7 August 2026, with private-channel estimates as high as 2,055 BTC.

~$88M Lost: COLDCARD & LULA Exploits | BlockSec Weekly
Security Insights

~$88M Lost: COLDCARD & LULA Exploits | BlockSec Weekly

During the week of July 27 to August 2, 2026, two notable security incidents caused roughly $88M in losses across Bitcoin and BNB Chain. The highlighted COLDCARD incident was a hardware-wallet firmware entropy failure: a build guard that checked whether an RNG configuration macro existed rather than whether it was enabled routed seed generation to a deterministic software fallback, enabling an attacker to recover affected seeds and sweep at least 1,370 BTC (~$88M) across a series of on-chain waves. The LULA token on BNB Chain lost ~$578K to a business-logic flaw where an attacker-reachable path could trigger its privileged `recycle()` function, pulling LULA out of a PancakeSwap V2 pair, resyncing its reserves to the manipulated balance, and draining its liquidity.

Secure Smart Contract Development (2) — How to Use Digital Signature and Use It Right in NFT (Markets)