commit e526fdaf395bc73ce1ae75ce8499aa7c25b4335e
parent 46a7cbf3f7606285c45437318608b3d725fc5298
Author: lash <dev@holbrook.no>
Date: Tue, 20 Dec 2022 08:28:50 +0000
Add docstrings to js library
Diffstat:
2 files changed, 123 insertions(+), 24 deletions(-)
diff --git a/js/manual_test_browser.js b/js/manual_test_browser.js
@@ -27,7 +27,6 @@ window.addEventListener('tokenBatch', async (e) => {
const mintedTokenData = await window.craftnft.getMintedToken(session, e.detail.tokenId, e.detail.batch);
console.debug('retrieved minted token data', mintedTokenData);
- //const isMintable = await window.craftnft.isMintAvailable(session, e.detail.tokenId, e.detail.batch);
if (mintedTokenData.mintable) {
const a = document.createElement('a');
a.setAttribute('onClick', 'uiMintToken("' + e.detail.tokenId + '", ' + e.detail.batch + ')');
diff --git a/js/src/engine.js b/js/src/engine.js
@@ -1,6 +1,11 @@
const Web3 = require('web3');
import MetaMaskSDK from '@metamask/sdk';
+/**
+ * Loads a chosen provider for the w3 inteface. Currently hard-coded to Metamask.
+ *
+ * @return {Object} Provider
+ */
function loadProvider() {
const mm = new MetaMaskSDK({injectProvider: false});
const w3_provider = mm.getProvider();
@@ -8,16 +13,39 @@ function loadProvider() {
return w3_provider;
}
+/**
+ * Returns a new web3 client instance.
+ *
+ * @return {Object} client
+ */
function loadConn(provider) {
const w3 = new Web3(provider);
return w3;
}
+/**
+ * Instantiates the token smart contract using the web3 client instance.
+ *
+ * @param {Object} client
+ * @param {Object} config
+ */
function loadContract(w3, config) {
const contract = new w3.eth.Contract(config.abi, config.contract);
return contract;
}
+
+/**
+ * Initialize the session object using config and client.
+ *
+ * Calls runner with client and session when initialization has been completed.
+ *
+ * @param {Object} client
+ * @param {Object} config
+ * @param {Object} session
+ * @param {Function} runner
+ * @throws free-form If contract cannot be loaded, or contract interface does not meet expectations.
+ */
async function startSession(w3, config, session, runner) {
const acc = await w3.eth.getAccounts();
session.account = acc[0];
@@ -34,11 +62,27 @@ async function startSession(w3, config, session, runner) {
runner(w3, session);
}
+
+/**
+ * Reload session with current states.
+ *
+ * @param {Object} session
+ * @return {Object} session (refreshed)
+ */
async function refreshSession(session) {
session.supply = await session.contract.methods.totalSupply().call({from: session.account});
return session;
}
+
+/**
+ * Visits callback with token spec as argument for every allocated token.
+ *
+ * @param {Object} client
+ * @param {Object} session
+ * @param {Function} callback
+ * @throws free-form If token does not exist
+ */
async function getTokens(w3, session, callback) {
let i = 0;
while (true) {
@@ -53,6 +97,15 @@ async function getTokens(w3, session, callback) {
}
}
+
+/**
+ * Create a new token allocation. Refer to the smart contract function allocate() for further details.
+ *
+ * @param {Object} session
+ * @param {String} tokenId (hex)
+ * @param {Number} amount
+ * @throws free-form If transaction is refused by the client
+ */
async function allocateToken(session, tokenId, amount) {
session.contract.methods.allocate('0x' + tokenId, amount).send({
from: session.account,
@@ -60,6 +113,16 @@ async function allocateToken(session, tokenId, amount) {
});
}
+
+/**
+ * Mint a new token from an existing allocation. Refer to the smart contract function mintFromBatchTo() for further details.
+ *
+ * @param {Object} session
+ * @param {String} tokenId (hex)
+ * @param {Number} batch
+ * @param {String} recipient of token mint
+ * @throws free-form If transaction is refused by the client
+ */
async function mintToken(session, tokenId, batch, recipient) {
const w3 = new Web3();
const address = await w3.utils.toChecksumAddress(recipient);
@@ -69,6 +132,16 @@ async function mintToken(session, tokenId, batch, recipient) {
});
}
+
+/**
+ * Assemble and return data describing a single minted token.
+ *
+ * @param {Object} session
+ * @psram {String} tokenId (hex)
+ * @param {Number} batch
+ * @return {Object}
+ * @throws free-form if token does not exist
+ */
async function getMintedToken(session, tokenId, batch) {
let o = {
mintable: false,
@@ -101,22 +174,20 @@ async function getMintedToken(session, tokenId, batch) {
return o;
}
-async function isMintAvailable(session, tokenId, batch) {
- let token = await session.contract.methods.token('0x' + tokenId, batch).call({from: session.account});
- if (token === undefined) {
- return false;
- }
- if (batch == 0) {
- if (token.count == 0) {
- return token.cursor == 0;
- }
- }
- if (token.cursor < token.count) {
- return true;
- }
- return false;
-}
+/**
+ * Generate a Token Id from a resolved Token Key.
+ *
+ * In the case of a Unique Token, this will be the same string.
+ *
+ * In case of a Batched Token, this will replace the batch and index embedded in the key with the remainder of the Token Id hash.
+ *
+ * @param {Object} session
+ * @param {String} tokenId (hex)
+ * @param {String} tokenContent (hex)
+ * @throws free-form If token does not exist
+ * @todo Function is a bit long, could be shortened.
+ */
async function toToken(session, tokenId, tokenContent) {
if (tokenId.substring(0, 2) == '0x') {
tokenId = tokenId.substring(2);
@@ -126,7 +197,6 @@ async function toToken(session, tokenId, tokenContent) {
tokenContent = tokenContent.substring(2);
}
- const v = parseInt(tokenContent.substring(0, 2), 16);
let data = {
tokenId: tokenId,
minted: false,
@@ -137,25 +207,28 @@ async function toToken(session, tokenId, tokenContent) {
sparse: false,
};
- let k = tokenId;
let issue = undefined;
+ // check whether it is an active minted token, and whether it's unique of batched.
+ // if not active we stop processing here.
+ const v = parseInt(tokenContent.substring(0, 2), 16);
if ((v & 0x80) == 0) {
+ // execute this only if token is batched.
if ((v & 0x40) == 0) {
issue = {};
- // TODO: the cap may be larger as we need to process for all batches, not matter whether theyre minted or not
- //const token = await session.contract.methods.token('0x' + tokenId, 0).call({from: session.account});
const state = await getBatches(session, tokenId);
data.batches = state.batches;
issue.cap = state.cap;
issue.count = state.count;
data.issue = issue;
- return data;
}
- }
+ return data;
+ }
data.minted = true;
+ // Fill in stats as applicable to whether Unique or Batched.
+ let k = tokenId;
issue = {}
if ((v & 0x40) == 0) {
k = tokenId.substring(0, 48) + tokenContent.substring(2, 18);
@@ -179,6 +252,14 @@ async function toToken(session, tokenId, tokenContent) {
return data;
}
+
+/**
+ * Retrieve current state of data for minted token.
+ *
+ * @param {Object} session
+ * @param {String} tokenId
+ * @return {Object} token
+ */
async function getTokenChainData(session, tokenId) {
const v = await session.contract.methods.mintedToken('0x' + tokenId).call({from: session.account});
@@ -187,6 +268,15 @@ async function getTokenChainData(session, tokenId) {
return mintedToken;
}
+
+/**
+ * Visit callback with token spec of every allocated token.
+ *
+ * @param {Object} session
+ * @param {String} tokenId (hex)
+ * @param {Function} callback
+ * @return {Object} summary of iteration.
+ */
async function getBatches(session, tokenId, callback) {
let token = await session.contract.methods.token('0x' + tokenId, 0).call({from: session.account});
if (token.count == 0 && callback !== undefined) {
@@ -203,7 +293,7 @@ async function getBatches(session, tokenId, callback) {
let cap = parseInt(token.count);
while (true) {
try {
- token = await session.contract.methods.token('0x' + tokenId, 1).call({from: session.account});
+ token = await session.contract.methods.token('0x' + tokenId, i).call({from: session.account});
} catch(e) {
break;
}
@@ -221,6 +311,16 @@ async function getBatches(session, tokenId, callback) {
};
}
+
+/**
+ * Check if the given address is the owner of the smart contract.
+ *
+ * Only the owner may allocate and mint tokens.
+ *
+ * @param {Object} session
+ * @param {String} address (hex)
+ * @return {Boolean} true if owner
+ */
async function isOwner(session, address) {
let owner = await session.contract.methods.owner().call({from: session.account});
@@ -231,6 +331,7 @@ async function isOwner(session, address) {
return address == owner;
}
+
module.exports = {
loadProvider: loadProvider,
loadConn: loadConn,
@@ -239,7 +340,6 @@ module.exports = {
getBatches: getBatches,
allocateToken: allocateToken,
mintToken: mintToken,
- isMintAvailable: isMintAvailable,
isOwner: isOwner,
getTokenChainData: getTokenChainData,
getMintedToken: getMintedToken,