Skip to content

Instantly share code, notes, and snippets.

@anonymoussprocket
Last active May 14, 2021 05:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymoussprocket/3310bb4683d0909e68a7edc927bdad33 to your computer and use it in GitHub Desktop.
Save anonymoussprocket/3310bb4683d0909e68a7edc927bdad33 to your computer and use it in GitHub Desktop.
Tezos FA1.2 token workflow sample

Tezos FA1.2 token workflow sample

The purpose of this script is to demonstrate common operations on an FA1.2 token on the Tezos platform using the ConseilJS library and Nautilus Cloud infrastructure.

Workflow Outline

Using

To run the script you'll need:

  • Four testnet accounts from the faucet.
  • nodejs 12.22

Run npm i to install dependencies and transpile TypeScript, run npm start to run the workflow.

import * as fs from 'fs';
import * as glob from 'glob';
import fetch from 'node-fetch';
import * as log from 'loglevel';
import { registerFetch, registerLogger } from 'conseiljs';
import { TezosConseilClient, TezosNodeWriter, Tzip7ReferenceTokenHelper, KeyStore, TezosNodeReader, Signer, TezosMessageUtils } from 'conseiljs';
import { KeyStoreUtils, SoftSigner } from 'conseiljs-softsigner';
const tezosNode = 'https://tezos-florence.cryptonomic-infra.tech/';
const conseilServer = { url: 'https://conseil-florence.cryptonomic-infra.tech:443', apiKey: 'api key from nautilus.cloud', network: 'florencenet' };
const networkBlockTime = 30 + 1; // 60 + 1 for mainnet
let deployerAccount: KeyStore;
let issuerAccount: KeyStore;
let keeperAccount: KeyStore;
let holderAccount: KeyStore;
let clencherAccount: KeyStore;
function clearRPCOperationGroupHash(hash: string) {
return hash.replace(/\"/g, '').replace(/\n/, '');
}
function initConseil() {
const logger = log.getLogger('conseiljs');
logger.setLevel('error', false);
registerLogger(logger);
registerFetch(fetch);
}
async function init() {
let faucetFiles: string[] = glob.sync('tz1*.json');
if (faucetFiles.length < 4) {
throw new Error('The demo needs four faucet accounts, please go to faucet.tzalpha.net to get them');
}
const deployerFaucetAccount = JSON.parse(fs.readFileSync(faucetFiles[0], 'utf8'));
deployerAccount = await KeyStoreUtils.restoreIdentityFromFundraiser(deployerFaucetAccount['mnemonic'].join(' '), deployerFaucetAccount['email'], deployerFaucetAccount['password'], deployerFaucetAccount['pkh']);
const issuerFaucetAccount = JSON.parse(fs.readFileSync(faucetFiles[1], 'utf8'));
issuerAccount = await KeyStoreUtils.restoreIdentityFromFundraiser(issuerFaucetAccount['mnemonic'].join(' '), issuerFaucetAccount['email'], issuerFaucetAccount['password'], issuerFaucetAccount['pkh']);
const holderFaucetAccount = JSON.parse(fs.readFileSync(faucetFiles[2], 'utf8'));
holderAccount = await KeyStoreUtils.restoreIdentityFromFundraiser(holderFaucetAccount['mnemonic'].join(' '), holderFaucetAccount['email'], holderFaucetAccount['password'], holderFaucetAccount['pkh']);
const clencherFaucetAccount = JSON.parse(fs.readFileSync(faucetFiles[3], 'utf8'));
clencherAccount = await KeyStoreUtils.restoreIdentityFromFundraiser(clencherFaucetAccount['mnemonic'].join(' '), clencherFaucetAccount['email'], clencherFaucetAccount['password'], clencherFaucetAccount['pkh']);
await Promise.all([
activateAccount(deployerAccount, deployerFaucetAccount['secret']),
activateAccount(issuerAccount, issuerFaucetAccount['secret']),
activateAccount(holderAccount, holderFaucetAccount['secret']),
activateAccount(clencherAccount, clencherFaucetAccount['secret'])
]);
await Promise.all([
revealAccount(deployerAccount),
revealAccount(issuerAccount),
revealAccount(holderAccount),
revealAccount(clencherAccount)
]);
console.log(`deployer secret key: ${deployerAccount.secretKey}`);
console.log(`deployer account hash: ${deployerAccount.publicKeyHash}`);
console.log(`issuer secret key: ${issuerAccount.secretKey}`);
console.log(`issuer account hash: ${issuerAccount.publicKeyHash}`);
console.log(`holder secret key: ${holderAccount.secretKey}`);
console.log(`holder account hash: ${holderAccount.publicKeyHash}`);
console.log(`clencher secret key: ${clencherAccount.secretKey}`);
console.log(`clencher account hash: ${clencherAccount.publicKeyHash}`);
return;
}
async function activateAccount(keystore, secret): Promise<string> {
const accountRecord = await TezosConseilClient.getAccount(conseilServer, conseilServer.network, keystore.publicKeyHash);
if (accountRecord !== undefined) { return accountRecord['account_id']; }
const signer = await SoftSigner.createSigner(TezosMessageUtils.writeKeyWithHint(keystore.secretKey, 'edsk'));
const nodeResult = await TezosNodeWriter.sendIdentityActivationOperation(tezosNode, signer, keystore, secret);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected activation operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
return conseilResult.pkh;
}
async function revealAccount(keystore): Promise<string> {
if (await TezosNodeReader.isManagerKeyRevealedForAccount(tezosNode, keystore.publicKeyHash)) {
return keystore.publicKeyHash;
}
const signer = await SoftSigner.createSigner(TezosMessageUtils.writeKeyWithHint(keystore.secretKey, 'edsk'));
const nodeResult = await TezosNodeWriter.sendKeyRevealOperation(tezosNode, signer, keystore);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected reveal operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
return conseilResult.source;
}
async function statOperation(groupid: string){
const result = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 7, networkBlockTime);
if (result['status'] === 'failed') {
console.log(`${result['kind']} ${groupid} ${result['status']} at block ${result['block_level']}`);
} else if (result['status'] === 'applied') {
let message = `${result['kind']} ${groupid} included in block ${result['block_level']} for ${result['consumed_gas']}g and ${result['paid_storage_size_diff']}f`
if ('originated_contracts' in result && result['originated_contracts'] != null && result['originated_contracts'].length > 0) {
message += ` new contract at ${result['originated_contracts']}`;
}
console.log(message);
} else {
console.log(JSON.stringify(result));
}
}
async function deployToken(keystore: KeyStore) {
const signer = await SoftSigner.createSigner(TezosMessageUtils.writeKeyWithHint(keystore.secretKey, 'edsk'));
const groupid = await Tzip7ReferenceTokenHelper.deployContract(tezosNode, signer, keystore, 100_000, keystore.publicKeyHash, true, 0);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
const tokenContractAddress = conseilResult['originated_contracts'];
console.log(`deployed token at ${tokenContractAddress} with ${groupid}`);
return tokenContractAddress;
}
async function getTokenRegistry(address: string){
const simplestorage = await Tzip7ReferenceTokenHelper.getSimpleStorage(tezosNode, address);
return simplestorage.mapid;
}
async function transferOwnership(keystore: KeyStore, tokenAddress, adminAddress: string) {
const signer = await SoftSigner.createSigner(TezosMessageUtils.writeKeyWithHint(keystore.secretKey, 'edsk'));
const groupid = await Tzip7ReferenceTokenHelper.setAdministrator(tezosNode, signer, keystore, tokenAddress, adminAddress, 100_000, 125_000, 1_000);
await statOperation(groupid);
console.log(`transferred token ownership to ${adminAddress} with ${groupid}`);
}
async function activateToken(keystore: KeyStore, tokenAddress: string) {
const signer = await SoftSigner.createSigner(TezosMessageUtils.writeKeyWithHint(keystore.secretKey, 'edsk'));
const groupid = await Tzip7ReferenceTokenHelper.activateLedger(tezosNode, signer, keystore, tokenAddress, 50_000, 125_000, 100);
await statOperation(groupid);
console.log(`activated token with ${groupid}`);
}
async function mintInitialSupply(keystore: KeyStore, tokenAddress: string, amount: number) {
const signer = await SoftSigner.createSigner(TezosMessageUtils.writeKeyWithHint(keystore.secretKey, 'edsk'));
const groupid = await Tzip7ReferenceTokenHelper.mint(tezosNode, signer, keystore, tokenAddress, 50_000, keystore.publicKeyHash, amount, 125_000, 100);
await statOperation(groupid);
console.log(`minted initial token supply with ${groupid}`);
}
async function issueTokens(keystore: KeyStore, tokenAddress: string, holder: string, amount: number) {
const signer = await SoftSigner.createSigner(TezosMessageUtils.writeKeyWithHint(keystore.secretKey, 'edsk'));
const groupid = await Tzip7ReferenceTokenHelper.transferBalance(tezosNode, signer, keystore, tokenAddress, 50_000, keystore.publicKeyHash, holder, amount, 125_000, 100);
await statOperation(groupid);
console.log(`issued tokens to ${holder} with ${groupid}`);
}
async function createNewAccount(source: KeyStore) {
const mnemonic = KeyStoreUtils.generateMnemonic();
const keystore = await KeyStoreUtils.restoreIdentityFromMnemonic(mnemonic);
const signer = await SoftSigner.createSigner(TezosMessageUtils.writeKeyWithHint(keystore.secretKey, 'edsk'));
const nodeResult = await TezosNodeWriter.sendTransactionOperation(tezosNode, signer, source, keystore.publicKeyHash, 1_000_000, 1_500);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected transaction operation with ${groupid}`);
await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
await revealAccount(keystore);
return keystore;
}
async function run() {
initConseil();
await init();
const tokenContractAddress = await deployToken(deployerAccount);
await transferOwnership(deployerAccount, tokenContractAddress, issuerAccount.publicKeyHash);
await activateToken(issuerAccount, tokenContractAddress);
await mintInitialSupply(issuerAccount, tokenContractAddress, 1_000_000);
await issueTokens(issuerAccount, tokenContractAddress, holderAccount.publicKeyHash, 1_000);
await issueTokens(issuerAccount, tokenContractAddress, clencherAccount.publicKeyHash, 1_000);
keeperAccount = await createNewAccount(deployerAccount);
}
run();
{
"name": "tezos-token-test",
"version": "0.0.2",
"description": "Integrated Tezos Token Test",
"scripts": {
"postinstall": "npm run build",
"build": "tsc *.ts",
"start": "node index.js"
},
"author": "anonymoussprocket",
"license": "UNLICENSED",
"engines": {
"node": ">=12.22.x",
"npm": ">=6.14.x"
},
"homepage": "https://gist.github.com/anonymoussprocket",
"dependencies": {
"@types/node": "14.14.35",
"conseiljs": "5.0.8-3",
"conseiljs-softsigner": "5.0.4-1",
"glob": "7.1.6",
"loglevel": "1.7.1",
"node-fetch": "2.6.1"
},
"devDependencies": {
"typescript": "3.8.3"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment