Aspen Javascript SDK
This is the Aspen Javascript SDK the Aspen publishing and trading platform (https://aspenft.io/).
Use this library to publish NFT collections and mint directly from your projects site or airdrop from backend services. Crypto and Fiat supported.
The SDK allows you to interact with our APIs and our Aspen-published smart contracts. Allowing you to integrate server-side definition and publishing of NFT contracts and configure NFT drops as well as client-side integration with the Aspen smart contracts to mint/claim NFTs for ERC721 and ERC1155 contracts. Features include:
- Metadata distribution backed by a centralised IPFS gateway backing on to decentralised IPFS
- Token metadata definition
- Time-based claim phases and conditions including:
- Phase start date
- Merkle-tree based allow lists (managed server-side)
- Token pricing (in native token, e.g. Ethereum)
- Server-side issuance including with dynamic content
- Contract features include:
- Legal terms definition and acceptance (blocking transfer when terms are not accepted by transferee)
- Client-side claiming with merkle proof of allowlist inclusion
- Native token and ERC20 token payment (as defined by claim phase)
- First-come-first-served sequential
tokenId
allocation for ERC721 - Claim phase limits and global or per token (ERC1155) supply limits
- Wallet cooldown period and maximum claim count
The SDK is packaged ECMAScript modules consumable by browsers and recent versions of NodeJS (versions >= 16).
Supported networks
Aspen and the Aspen SDK currently support the Ethereum based networks:
- Ethereum (mainnet)
- Polygon (matic)
And their respective test networks
Installation
From your Javascript project root:
# pnpm
pnpm add @monaxlabs/aspen-sdk
# yarn
yarn add @monaxlabs/aspen-sdk
# npm
npm install @monaxlabs/aspen-sdk
Core packages
The Aspen SDK is split into a client-side smart contract library and API clients requiring secrets that should only be run from secure backend services.
- Client-side contracts library:
@monaxlabs/aspen-sdk/dist/contracts
- Server-side API library:
@monaxlabs/aspen-sdk/dist/apis
We also package a TheGraph subgraph at @monaxlabs/aspen-sdk/dist/claimgraph
to provide tracking of claims during a live drop.
Authentication for APIs
Our publishing and gating APIs can be authenticated using a basic auth username and password.
To do so you must obtain credentials (please use this request form) and include them in an JSON object with this schema:
{
"production": {
"publishing": {
"baseUrl": "https://publishing.aspenft.io",
"name": "username",
"password": "password"
},
"gating": {
"baseUrl": "https://identity.aspenft.io",
"name": "username",
"password": "password"
}
}
}
Then you can authenticate (once, globally): with:
import { authenticateAll, authenticateAllFromFile } from '@monaxlabs/aspen-sdk/dist/apis';
const creds = {
production: {
publishing: {
baseUrl: 'https://publishing.aspenft.io',
name: 'username',
password: 'password',
},
gating: {
baseUrl: 'https://identity.aspenft.io',
name: 'username',
password: 'password',
},
},
};
const environment = 'production';
async function authenticate(): Promise<void> {
await authenticateAll(environment, creds);
await authenticateAllFromFile(environment, 'credentials.json', 'CREDENTIALS_ENV_VAR_NAME');
}
Interacting with smart contracts
Interacting with the Aspen suite of smart contracts is handled via the CollectionContract
class. In order to use it pass an ethers.js compatible Provider
.
The CollectionContract
is able to dynamically introspect contracts on live networks and determine their feature suite. It abstracts over both ERC721 and ERC1155 contracts.
You can obtain one via a variety of browser wallet plugins, most commonly MetaMask or using some test helpers and a local private key.
For testing and integration purposes you can provide Ethereum web3 JSON-RPC URLs, and local private keys to our getProvider
function. The provider config is defined by:
{
"providerUrls": {
"Mumbai": "",
"Mainnet": "",
"Goerli": "",
"Polygon": ""
},
"privateKeys": {
"Mumbai": "",
"Mainnet": "",
"Goerli": "",
"Polygon": ""
}
}
Instantiating a CollectionContract
You can establish a connection with a contract by passing a provider and the contract address to the CollectionContract
constructor. When contract.load()
as called the contract is queried using some introspection interfaces to discover which Aspen features are supported. Features are individual units of functionality that are semantically versioned.
import { CollectionContract } from '@monaxlabs/aspen-sdk/dist/apis';
async function getCollectionContract(): Promise<CollectionContract> {
const network = 'Mumbai';
const providerConfig = await getProviderConfig('providers.json');
const provider = await getProvider(network, providerConfig);
const userSigners = generateAccounts(numAccounts, { mnemonic: demoMnemonic, provider });
const contract = await CollectionContract.from(provider, collectionAddress);
}
As well as discovering the specific features implemented the token standard implemented by the contract is also established which is one of:
Interacting with features
There are five Aspen NFT contract feature families supported by the Aspen SDK:
- Metadata: for dealing with contract-level and token-level metadata and assets.
- Issuance: for issuing/minting tokens according to phase conditions or the issuing privileges of an account.
- Agreements: for viewing and accepting legal terms associated with the ownership of an NFT.
- Royalties: for calculating and paying royalties on NFT sales and transfers.
- Ownable:: for associating a collection owner/creator for the purposes of marketplace listings.
Each of these features comprises a set of utility methods that interact with contracts. Where possible we maintain backward-compatibility of certain feature functions across previous versions of our smart contracts. As we develop and improve our contracts newer collections may have additional functionality.
You can interact with features by accessing the corresponding feature property on the CollectionContract
. Some examples:
import { NATIVE_TOKEN } from '@monaxlabs/aspen-sdk/dist/contract';
async function main(): Promise<void> {
const contract = await CollectionContract.from(provider, collectionAddress);
const isSupported: boolean = contract.conditions.supported;
const tokenId = 0;
if (isSupported) {
const conditions = await contract.conditions.getState(receiver, tokenId);
if (conditions.claimState !== 'ok') {
throw new Error(`Not okay to mint based on user restrictions and claim phase`);
}
const tokenUri = await contract.metadata.getTokenUri(tokenId);
await contract.agreements.acceptTerms(signer);
contract.claims.claim(
signer,
{
receiver,
tokenId,
quantity,
currency: conditions.currency,
pricePerToken: conditions.pricePerToken,
proofs: conditions.allowlist.proofs,
proofMaxQuantityPerTransaction: conditions.allowlist.proofMaxQuantityPerTransaction,
},
);
}
}
Core CollectionContract
functions
Metadata
Issuance
Phases
Our NFT contracts support a linear (one-at-a-time) sequence of claim phases. Each phase can have its own set of rules for how the contract will mint tokens. Each phase has a defined start time and will last until the next consecutive phase begins if a next phase is defined. The phase is defined by a ClaimCondition
which contains (amongst other things):
- The phase start time
- The token price and currency, if any
- The allowlist, if any
- The maximum claimable supply for that phase
You can see an example phases definition here.
In order to know whether it is possible for a particular (Ethereum) account to claim you must know the active claim condition. In particular, you must know:
- The price of the token (to send as native token or approve in for transfer by the NFT contract in the case of ERC20)
- The currency (etiher
NATIVE_TOKEN
or an ERC20 contract address) - Whether there is any claimable supply left (strictly you do not need to know this, since if the supply is exhausted the transaction will revert, but this results in wasting the cost of the gas for the transaction)
- Whether the phase has an allowlist (in which case you must provide a proof of the claimant account's inclusion on that list)
Integration guide
Here we describe integrating the headline features of the SDK. Namely defining and publishing an NFT drop collection and allowing users to mint from a client-side browser application.
You can see working code examples for the flows listed below in the SDK monorepo examples directory.
Define and publish a collection
Collections can be defined and deployed by our publishing API. The steps are as follows:
- Define the type of collection
- Define claim phases and conditions
- Define tokens and metadata
- Deploy the collection
Each of these steps is achieved by calling various endpoints on our publishing API. The OpenAPI specs for these APIs are available on NPM at @monaxlabs/spec. Our Typescript APIs are generated from these specs with some additional helpers
Once authenticated as above you can access each of the publishing APIs service objects and call the endpoints statically. The process is:
import {
Chain,
CollectionActionsService,
CollectionInfoService,
CollectionResponse,
CollectionService,
CollectionTokenPhasesService,
} from '@monaxlabs/aspen-sdk/dist/apis/publishing';
import { waitForCompletion } from './waiter';
export async function deployCollection(): Promise<CollectionResponse> {
const contractName: 'CedarERC721Drop' | 'CedarERC1155Drop' = 'CedarERC721Drop';
const collection = await CollectionService.postCollection({
requestBody: {
contractName,
},
});
const collectionGuid = collection.guid;
await TokenService.postToken({});
CollectionService.postCollectionRoyaltyrecipients({});
const { web2Url } = await CollectionService.postCollectionTerms({ guid: collectionGuid });
await CollectionService.postCollectionTermsEnable({ guid: collectionGuid, status: true });
await CollectionInfoService.postCollectionInfo({});
await CollectionActionsService.postCollectionDeploy({ guid: collectionGuid });
await waitForCompletion(CollectionActionsService.getCollectionDeploy, { guid: collectionGuid });
await CollectionActionsService.postCollectionMint({ guid: collectionGuid });
await waitForCompletion(CollectionActionsService.getCollectionMint, { guid: collectionGuid });
return CollectionService.getCollectionById({ guid: collectionGuid });
}
Mint via crypto payment
The aim of this integration is to place a 'mint' button on our drop page. The necessary steps for claiming a token using the SDK is given below, but a fully worked up example in React can be found in our SDK examples directory here. Our SDK is agnostic of any Javascript framework you choose to use and should work in the same way within a browser or NodeJS environment.
import { CollectionContract, parse, Address, NATIVE_TOKEN } from '@monaxlabs/aspen-sdk/dist/contracts/address';
import { BigNumberish } from 'ethers';
async function main(contract: CollectionContract): Promise<void> {
const claimingAccount: Address = parse(Address, '0x92380354b9f2334a9c78c0686645db04d52972bc');
const tokenId: null | BigNumberish = 2;
const conditions = await contract.conditions.getState(claimingAccount, tokenId);
if (conditions.claimState !== 'ok') {
throw new Error(`Not okay to mint based on user restrictions and claim phase`);
}
await contract.claims.claim(
signer,
{
conditionId: conditions.activeClaimConditionId
receiver: claimingAccount,
tokenId,
quantity: BigNumber.from(1),
currency: conditions.currency,
pricePerToken: conditions.pricePerToken,
proofs: conditions.allowlist.proofs,
proofMaxQuantityPerTransaction: conditions.allowlist.proofMaxQuantityPerTransaction,
}
);
}
Issue a token (server-side)
Tokens can be issued using the publishing API directly. This skips claim conditions and allows issuing a token based on a fiat payment provider or for dynamics use cases.
import { authenticate, issueToken, PublishingAPI } from '@monaxlabs/aspen-sdk/dist/apis';
async function main(): Promise<void> {
await authenticate(PublishingAPI.OpenAPI, {
baseUrl: PUBLISHING_API_BASEURI,
name: PUBLISHING_API_USERNAME,
password: PUBLISHING_API_PASSWORD,
});
await issueToken(collectionGuid, {
to: wallet,
tokenId: Number.parseInt(tokenId),
});
}
See a complete webhook example here
Examples
Please see https://github.com/monax/aspen-sdk for working code examples and their documentation.