Hang SDK is on a mission to help developers create DAPPs with ease and interact with Hang deployed smart contracts.
node >= 10.0
<script src="https://cdn.jsdelivr.net/npm/hang-sdk/dist/index.js"></script>
Install the SDK
npm i hang-sdk
// OR
yarn add hang-sdk
It is possible to load the SDK via a <script></script>
tag. This will expose two variables on the Window
object by which you can call the methods of the SDK.
- First load the SDK from the CDN (do this in its own script tag)
- Next we define an
sdk
variable & set the projectslug
- Finally we listen for a
STATE_CHANGE
event. The first event of this kind will indicate the SDK is ready to work with.
<script src="https://cdn.jsdelivr.net/npm/hang-sdk/dist/index.js"></script>
<script>
const sdk = new HangWalletPlugin({
slug: 'jerry-garcia-2022-04-26-468a',
});
console.log(sdk);
sdk.events.on('STATE_CHANGE', () => sdk.mint());
</script>
Otherwise interacting with the SDK is the same without regard to whether you are using a React or Script Tag implementation
You can check out a working example of a script tag implementation at examples/script-tag.
You can take a look at a working bare bones SDK
powered demo ui here. You can check out the code of the demo react app at examples/react-app.
Below we walk through the main points of the example implementation. Import the HangWallet
plugin and the Types
for the state change events that are available.
import {
HangWalletPlugin,
IStateChangeEventParams,
ITransactionCompletedEventParams,
IWalletConnectedEventParams,
IErrorEventParams,
ITransactionSubmittedEventParams,
} from 'hang-sdk';
Here we initialize the SDK
with the unique project slug
in React
.
const sdk = useMemo(
() =>
new HangWalletPlugin({
slug: 'prefill-info-test-live-from-home-2021-12-08-f88d',
}),
[]
);
The SDK
now exposes set of methods that allow you to interact with the Hang
smart contract, access account & transaction information, and listen for relevant state changes and errors. You can use all of these methods irrespective of whether you imported via Script Tag or as a Node Module.
For instance you can:
// retrieve the current wallet's balance
const balance = await sdk.balanceOfCurrentWallet();
// retrieve the total available NFTs to mint
const totalMintable = await sdk.fetchTotalMintable();
// retrieve the total number of NFTs currently minted
const totalMinted = await sdk.fetchTotalMintedPadded();
// retrieve the current price of the NFT
const currentPrice = await sdk.fetchCurrentPriceFormatted();
// trigger Fiat purchase flow with Crossmint's custodial wallet system
sdk.crossMint()
Here we are using the above SDK
methods to fetch the contract related data, and the handling methods to update the state of the react app.
const handleStateChange = useCallback(
const [metadata, setMetadata] = useState<IMetadata>({
totalMintable: -1,
totalMinted: -1,
currentPrice: '',
});
// ...
async ({ isReady }: IStateChangeEventParams) => {
setLoadingText(isReady ? '' : 'Unable to initialize sdk');
setEvents((prevState) =>
prevState.concat({
name: 'STATE_CHANGE',
params: { isReady },
as: InfoIcon,
color: 'green.500',
})
);
const totalMintable = await sdk.fetchTotalMintable();
const currentPrice = await sdk.fetchCurrentPriceFormatted();
const totalMinted = await sdk.fetchTotalMintedPadded();
setMetadata((prevState) => ({
...prevState,
totalMintable,
currentPrice,
totalMinted,
}));
},
[sdk]
);
// ...
useEffect(() => {
sdk.events.on('STATE_CHANGE', handleStateChange);
return () => {
sdk.events.removeAllListeners();
};
}, []);
You can listen for transaction related events via the SDK
. Below is an example where each class of event is routed throught to a handling function in React for use in the app. The output of the dispatched events are typed. You can see an example of how to import the corresponding event output types above.
useEffect(() => {
sdk.events.on('ERROR', handleError);
sdk.events.on('TRANSACTION_SUBMITTED', handleTransactionSubmitted);
sdk.events.on('TRANSACTION_COMPLETED', handleTransactionCompleted);
sdk.events.on('WALLET_CONNECTED', handleWalletConnected);
sdk.events.on('WALLET_CHANGED', handleWalletChanged);
return () => {
sdk.events.removeAllListeners();
};
}, []);
The SDK
defines other useful methods. Here is an example where mint()
is called and passed a quantity of NFTs to create.
sdk.mint(quantity);
The user can immediately mint using a credit or debit card even if they do not have a crypto wallet set up already. To trigger the Crossmint minting flow just call the sdk.crossMint()
method. All of the necessary options will pre-populated by the SDK
.
Here is an example:
<Button
onClick={() => {
sdk.crossMint();
}}
>
Crossmint
</Button>
If you want to see how all of this comes together to form a working UI you can see a React App example examples/react-app.
To run the app, navigate to the react app directory & run:
npm start
In your browser navigate to: http://localhost:3000/
Create an instance of the SDK
by passing the unique project slug
and an options object for the web3Modal
UI. See the available options for the modal.
The project slug
specifies which contract and network to use.
const sdk = useMemo(
() =>
new HangWalletPlugin({
slug: 'prefill-info-test-live-from-home-2021-12-08-f88d',
}),
[]
);
Call mint to create an NFT(s) on chain and pay for it with the funds from the connected wallet. This will trigger a transaction approval flow in the connected wallet.
const quantity = 2
sdk.mint(quantity)
Call crossMint to trigger NFT minting via Crossmint's service. A user can pay via credit or debit card and manage the NFT in a custodial wallet.
sdk.crossMint()
Call fetchTotalMintable()
to get the total number of NFTs remaining to be minted.
const totalMintable = await sdk.fetchTotalMintable();
Call fetchCurrentPriceFormatted()
to get the price of the NFT as a formatted string.
const currentPrice = await sdk.fetchCurrentPriceFormatted();
Call fetchCurrentPrice()
to get the price of the NFT as a number.
const currentPrice = await sdk.fetchCurrentPrice();
Call fetchBalanceOfCurrentWallet()
to get the amount of Eth
as a number.
const balance = await sdk.balanceOfCurrentWallet();
The SDK
dispatches 5 kinds of events.
- errors
- transaction submissions
- transaction completions
- wallet connections
- wallet changes
This events publishes an error object with a message
and a type
.
Here is the list of error types which the SDK
will dispatch.
PROJECT_INFO_FETCH_ERROR
CANNOT_MINT
PURCHASE_DISABLED
INSUFFICIENT_ETH_AMOUNT
EXCEEDS_MAX_SUPPLY
GAS_FEE_NOT_ALLOWED
EXCEEDS_INDIVIDUAL_SUPPLY
PRESALE_INACTIVE
CANNOT_MINT_PRESALE
IErrorEventParams
This event publishes a single property isReady
which you would use to determine if the SDK
has initialized properly.
IStateChangeEventParams
This event publishes the hash of the submitted transactionHash
prior to the completion of transaction.
ITransactionSubmittedEventParams
This event is published upon the completion of a transaction and provides a reciept
object which contains properies the detail the state of the completed transaction on chain.
ITransactionCompletedEventParams
This event is published upon the completion of a transaction and provides a reciept
object which contains properies the detail the state of the completed transaction on chain.
ITransactionCompletedEventParams
This event is published upon the completion of a transaction and provides a reciept
object which contains properies that detail the state of the completed transaction on chain.
IWalletConnectedEventParams
This event is published when the user changes which account they have connected to the SDK
. It will not capture if the user changes the network that the Account is to use. However the SDK
will switch the network to the correct network when the mint
function is called and an actual transaction is triggered. Switching networks will then require an in wallet approval from the user.
There is no type interace for this event as no other information is passed along with the event.
To solve for missing Node Polyfills if you are using React-Scripts/Webpack v5 and above (which would be the case with a fresh install of Create React App) follow the steps below to fix this by installing react-app-rewired and adding a config which overrides the Create React App Webpack config without ejecting it.
Install react-app-rewired
npm install --save-dev react-app-rewired
Install the missing dependencies
npm install --save-dev crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer process
In the root of the project add a config that will override the CRA Webpack config file.
const webpack = require('webpack');
module.exports = function override(config) {
const fallback = config.resolve.fallback || {};
Object.assign(fallback, {
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"assert": require.resolve("assert"),
"http": require.resolve("stream-http"),
"https": require.resolve("https-browserify"),
"os": require.resolve("os-browserify"),
"url": require.resolve("url")
})
config.resolve.fallback = fallback;
config.plugins = (config.plugins || []).concat([
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
})
])
return config; }
Override package.json to include the webpack configuration. Replace react-scripts with react-app-rewired scripts for start, build, & test
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},