Highlight Smart Contracts: Cost

Prerequisite reading -

Interacting with blockchains costs money. This is one of the most common barriers to entry for potential users. Typically, to mint an NFT, a user needs:

  1. A pre-existing blockchain account

  2. A pre-existing browser wallet with that blockchain account

  3. A pre-existing sufficient amount of the native gas token of that blockchain in that account

  4. A pre-existing sufficient amount of the on-chain currency used to purchase the NFT in that account

At Highlight we’ve eliminated the need for the first three, and streamlined the experience of the last. The third entails a significantly higher barrier to entry than the first two, especially if the NFT presides on an L2. This is because the user will likely need to bridge ETH from Ethereum to wETH on the L2, and then further convert that to the native gas token on the L2 (eg. Matic on Polygon). This occurs when fiat gateways to the L2 are limited, or with users who wish to fund their purchase entirely from on-chain sources. In this blog, we will take a holistic look at cutting costs when interacting with the Highlight protocol. To preface, we break down gas costs into 5 components:

Gas Law

The gas law applies for EVM-based blockchains using EIP-1559. We created this law internally to systematically approach optimization of the gas experience.

gas cost = ((gas units / operation) x (number of operations) x (priority fee + base fee)) / who pays

gas units / operation: This has to do with how the contracts are made. Optimizing means optimizing solidity code, modifying the protocol, or modifying when we perform certain operations.

number of operations: How many mints, deploys, token purchases, etc. We can partition by platform, community, user, etc.

priority fee: Gas configuration is made up of 3 values: baseFee, maxPriorityFee, maxFee. We can ignore maxFee for simplicity. maxPriorityFee is the amount given to the miners. A miner has more incentive to mine transactions with a higher maxPriorityFee.

base fee: baseFee is the network’s base fee at the time. The amount of gwei you pay per gas unit has to be more than the base fee, otherwise the transaction will not process. Average gas cost on the network is made up of the base fee, and the priority fee. An average gas cost per unit could have drastically different meanings depending on its composition.

who pays: Self-explanatory.

In this blog we will largely discuss gas units / operation and number of operations optimizations as they concern the smart contract protocol. However, we will also briefly cover some optimizations we’ve implemented in our services for the latter three components of the gas law. This includes some optimizations we employ to pay for all of our users’ gas on Polygon.

Protocol Optimizations

1155 Standard

The first optimization is hidden in plain sight. It’s the choice of the ERC1155 standard. The ERC1155 standard allows creators to mint multiple copies of a token at virtually the same cost as minting a 1/1 NFT. So, creators can distribute membership and collectible tokens that don’t need to be different, much more efficiently. This is a number of operations optimization.

Clones and Proxies

To minimize deployment costs, we make use of upgradeable proxy contracts and non-upgradeable clone contracts that delegate incoming calls to their respective implementations. Since our implementations are very large, we use clones and proxies, which in some cases are 10x cheaper to deploy than their respective implementations. This means that we end up using 1/10th the gas typically required for things like community deployments and token manager deployments! This is a gas units / operation optimization.

Gas-less / Meta-Transactions

One of the most common problems with dApps is the gas cold-start problem. Users who don’t have the native gas token of the blockchain they’re interacting with cannot interact with the blockchain. At Highlight, it is critical that all users (even ones without an existing blockchain wallet) can interact with and create communities. So, two of our core smart contract segments accept meta-transactions - all communities, and the central royalty manager. This allows us to offer features where we continue to pay for our users’ actions, where the contract interaction is restricted to the user invoking the action. For example, modifying parts of a community’s royalty split is restricted to the community creator on-chain. What’s key at Highlight is being able to offer a protocol that’s decentralized without compromising our web2-like UX and robust transaction handling. In our system, we support meta-transactions making use of both EIP-2771, and older constructs, all of which make use of EIP-712. Meta-transactions are a who pays optimization.

The only diagram you'll need to understand 90% of meta-transaction paradigms
The only diagram you'll need to understand 90% of meta-transaction paradigms

On-chain Payments

Highlight operates at the intersection of web2 and web3. It was important for us to support crypto payments in the most frictionless manner possible. The easiest way to access liquidity on Polygon is to bridge Ether from Ethereum to wETH on Polygon. Once someone has wETH, they can swap wETH for Matic, which is the native gas token on Polygon. Since many users are interacting with Polygon for the first time when using Highlight, we wanted to reduce the number of steps that it took to purchase a creator’s token via crypto. So, the first constraint of our system was that users could purchase tokens in wETH, without needing Matic at all, meaning our contracts needed to support meta-transactions/gas-less transactions.

CentralPaymentsManager is a smart contract acting as a thin layer that facilitates payments for communities. Users purchase tokens simply by signing two messages via their browser wallet, authorizing the purchase. The purchaser is immediately charged the wETH they are using to purchase the tokens. The two messages the purchaser signs are what we’ve coined “impersonation packets” which allow Highlight to impersonate the purchaser, and access the purchaser’s wETH on their behalf. We use these, along with a packet that Highlight signs, to process the entire transaction.

Users don’t have to approve each community and each currency to transact their money for NFTs. This is an improvement in four ways:

  • Cost - The system saves gas because users don’t have to set approvals for every community and every currency. This converts the expected number of operations for approval system-wide, from a(n + m) operations to O(1), where a is number of users, n is number of communities, m is number of currencies. Further, users don’t need MATIC (the chain’s native gas currency) to purchase tokens, they just need wETH or the listing currency in their wallet.

  • Security - Users often get burned by hanging approvals. Our system processes purchases on a per-transaction basis. Since we can bypass the leaky approval process, this prevents the user from being exposed to any unnecessary liability across a lifetime of transactions.

  • Latency - Users don’t have to send approval transactions before purchase transactions. All they have to do is sign 2 messages on their client, gasless-ly.

  • Creator UX - With this system, listings can remain off-chain, and are updatable without mutating on-chain state. So, creators can update listing parameters such as token price however often they want, without having to execute transactions on the blockchain. This saves a ton of gas, as we’ve removed an O(n) number of operations, where n is the number of listings (tokens made available for sale) ever created through Highlight.

On-chain payments went live on Highlight in June 2022. We facilitate credit card payments for NFTs in a similar manner. They are a number of operations and a who pays optimization.

Royalties

The royalties portion of the protocol forks the 0xSplits protocol. 0xSplits emphasizes gas-efficiency in two different ways:

  1. The smart contract code is hyper-optimized for gas-efficiency with neat tricks. This is a gas units / operation optimization.

  2. Royalties are pulled in two steps. The first is on a per-community basis, and the second is on a per-user basis. Consider a user that has 5 communities on Highlight. Once all royalties across the 5 communities are distributed and allocated, the user can pull their royalties accrued across all 5, in one transaction. One of our system smart contracts, APIProxy, bundles royalty collection in one transaction and in different configurations for optimal UX and gas-efficiency. This is a number of operations optimization.

APIProxy

Here’s a stripped down interface of the royalty collection portion of APIProxy.

function withdrawRoyaltiesOwedForUserOnCommunity(
  address community,
  address account,
  ERC20[] calldata currencies
) external {}
 
function withdrawRoyaltiesOwedForAllRecipientsOnCommunity(address community, ERC20[] calldata currencies) external {}
 
function getERC20RoyaltiesOwedForAllRecipientsOnCommunity(address community, ERC20 currency)
  external
  view
  returns (
    address[] memory,
    uint256[] memory,
    uint256[] memory
  ) {}
 
function getNativeRoyaltiesOwedForAllRecipientsOnCommunity(address community)
  external
  view
  returns (
    address[] memory,
    uint256[] memory,
    uint256[] memory
  ) {}

Efficient Token Type Encoding

The Highlight protocol currently supports two types of community tokens: membership and collectible. Membership tokens authorize access to communities, and collectible tokens serve other use-cases. Both of these token types are encoded on-chain. To efficiently encode the token’s type, instead of using a mapping directly from token id to type, we’ve implemented an alternating id scheme that allows one to determine the token’s type only from the id. This holistically saves the gas costs associated with storage, by avoiding an extra mapping.

Membership tokens are tokens with ID 1-100, 201-300, 401-500 and so on. Collectible tokens are tokens with ID 101-200, 301-400, 501-600 and so on. The scheme alternates every 100 tokens, rather than alternating on parity due to legacy from an early implementation of the community proxy. This is a gas units / operation optimization.

Airdrops

Communities support built-in airdrops. Rather than having to submit n transactions to facilitate n airdrops to n recipients, we exposed a function that lets creators atomize airdrops. Thus, they can submit one airdrop transaction to n recipients. They can do this for any community token they mint, at any time. This is a major number of operations optimization.

function safeBatchTransferFromMultipleRecipients(
  address from,
  address[] calldata to,
  uint256[] calldata ids,
  uint256[] calldata amounts,
  bytes calldata data
) public virtual nonReentrant whenNotPaused {
  address msgSender = _msgSender();
  require(from == msgSender || isApprovedForAll(from, msgSender), "ERC1155: caller unauthorized");
  // not caching length here due to stack depth
  for (uint256 i = 0; i < to.length; i++) {
    _safeBatchTransferFrom(msgSender, from, to[i], ids, amounts, data);
  }
  for (uint256 i = 0; i < ids.length; i++) {
    address manager = _tokenToManager[ids[i]];
    if     (IERC165Upgradeable(manager).supportsInterface(type(IPostSafeTransfer).interfaceId)) {
      for (uint256 j = 0; j < to.length; j++) {                                                 IPostSafeTransfer(manager).postSafeTransferFrom(msgSender, from, to[j], ids[i], amounts[i], data);
      }
    }
  }
}
Made available by the airdrop function, although creators can submit any set of recipients for an airdrop
Made available by the airdrop function, although creators can submit any set of recipients for an airdrop

Miscellaneous Optimizations

Modifying template contracts: The Community smart contract makes use of template OpenZeppelin contracts such as ERC1155Upgradeable. We’ve forked some of these smart contracts and introduced code optimizations, removing superfluous code. These are gas units / operation optimizations.

Code optimizations: Techniques such as caching array lengths, variable packing, minimizing SLOADs and more help here. These are gas units / operation optimizations. We highly recommend reading the Ethereum Yellowpaper to gain a fundamental understanding of neat code-based gas optimizations.

Global token managers: We’ve recently modified our protocol to support global TokenManagers, which are stateless TokenManagers that are singleton system smart contracts that can be reused across communities. This helped us eliminate 10% of the gas required to set-up a community as we could avoid deploying a new TokenManager clone for each community. This is a gas units / operation optimization.

Thanks for reading. Stay tuned for our next instalment, which will cover how Highlight smart contracts are interoperable and empower creator provenance. Explore the protocol directly here. Interested in working on cutting edge smart contract architecture and web3 infrastructure? We’re hiring.

Subscribe to Highlight
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.