Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Tezos User Workflows with ConseilJS

Purpose

Single script to test common Tezos user workflows. Additionally this sample illustrates the use of ConseilJS.

Environment

This tutorial requires nodejs 12.16+ & npm. It's highly recommended to install those via nvm.

Steps

Create a new directory for the files included in this gist. Run the following commands and observe the console output.

  • Create an account with Nautilus Cloud and fill in the missing data for tezosNode and conseilServer variables.
  • Get a faucet account save it in the same directory. The file name is expected to be in the format "tz1*.json"
  • nvm use 12.16
  • npm install
  • npm start

Other Links

Confirmed Environment Configurations

  • macOS 10.15.3, nodejs v12.16.3, conseiljs 0.4.2, typescript 3.8.3
  • macOS 10.15.4, nodejs v12.16.3, conseiljs 0.4.3-beta.1, typescript 3.8.3
import * as fs from 'fs';
import * as glob from 'glob';
import { TezosConseilClient, TezosWalletUtil, TezosNodeWriter, BabylonDelegationHelper, setLogLevel, TezosParameterFormat, KeyStore, OperationKindType, TezosNodeReader, TezosContractIntrospector } from 'conseiljs';
setLogLevel('debug');
const tezosNode = '<tezos-node-url>';
const conseilServer = { url: '<conseil-indexer-url>', apiKey: '<APIKEY_from_nautuilus.cloud>', network: 'carthagenet' };
const networkBlockTime = 30 + 1;
let faucetAccount = {};
let keystore: KeyStore;
let contractAddress: string;
let delegationContractAddress: string;
const bakerAddress = 'tz1VxS7ff4YnZRs8b4mMP4WaMVpoQjuo1rjf';
const anotherBakerAddress = 'tz3gN8NTLNLJg5KRsUU47NHNVHbdhcFXjjaB';
const accountAddress = 'tz1iwTKp5NEdDJSjNVLcYtB7AdELQvoS5tfS';
const anotherAccountAddress = 'tz1QmzkDPazvKYtLMS5Sxbz41vxBmykmk2TU';
function clearRPCOperationGroupHash(hash: string) {
return hash.replace(/\"/g, '').replace(/\n/, '');
}
async function initAccount(): Promise<KeyStore> {
console.log('~~ initAccount');
let faucetFiles: string[] = glob.sync('tz1*.json');
if (faucetFiles.length === 0) {
throw new Error('Did not find any faucet files, please go to faucet.tzalpha.net to get one');
}
console.log(`loading ${faucetFiles[0]} faucet file`);
faucetAccount = JSON.parse(fs.readFileSync(faucetFiles[0], 'utf8'));
const keystore = await TezosWalletUtil.unlockFundraiserIdentity(faucetAccount['mnemonic'].join(' '), faucetAccount['email'], faucetAccount['password'], faucetAccount['pkh']);
console.log(`public key: ${keystore.publicKey}`);
console.log(`secret key: ${keystore.privateKey}`);
console.log(`account hash: ${keystore.publicKeyHash}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
return keystore;
}
async function activateAccount(): Promise<string> {
console.log(`~~ activateAccount`);
const accountRecord = await TezosConseilClient.getAccount(conseilServer, conseilServer.network, keystore.publicKeyHash);
if (accountRecord !== undefined) { return accountRecord['account_id']; }
const nodeResult = await TezosNodeWriter.sendIdentityActivationOperation(tezosNode, keystore, faucetAccount['secret']);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected activation operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Activated account at ${conseilResult.pkh}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
return conseilResult.pkh;
}
async function revealAccount(): Promise<string> {
console.log(`~~ revealAccount`);
if (await TezosNodeReader.isManagerKeyRevealedForAccount(tezosNode, keystore.publicKeyHash)) {
return keystore.publicKeyHash;
}
const nodeResult = await TezosNodeWriter.sendKeyRevealOperation(tezosNode, keystore);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected reveal operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Revealed account at ${conseilResult.source}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
return conseilResult.source;
}
async function sendTransaction() {
console.log(`~~ sendTransaction: 500000µtz from ${keystore.publicKeyHash} into ${accountAddress}`);
const nodeResult = await TezosNodeWriter.sendTransactionOperation(tezosNode, keystore, accountAddress, 500000, 1500, '');
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected transaction operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Completed transfer of ${conseilResult.amount}µtz`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
}
async function delegatePrimaryAccount() {
console.log(`~~ delegatePrimaryAccount`);
const nodeResult = await BabylonDelegationHelper.setDelegate(tezosNode, keystore, keystore.publicKeyHash, bakerAddress, 20000);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected delegation operation with ${groupid}`);
await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
}
async function deployDelegationContract(): Promise<string> {
console.log(`~~ deployDelegationContract: from ${keystore.publicKeyHash} to ${anotherBakerAddress} with 500000µtz`);
const nodeResult = await BabylonDelegationHelper.deployManagerContract(tezosNode, keystore, anotherBakerAddress, 20000, 500000);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected origination operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Originated contract at ${conseilResult.originated_contracts}, delegated to ${conseilResult.delegate}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
return conseilResult.originated_contracts;
}
async function depositDelegatedFunds() {
console.log(`~~ depositDelegatedFunds: 5 xtz from ${keystore.publicKeyHash} into ${delegationContractAddress}`);
const nodeResult = await BabylonDelegationHelper.depositDelegatedFunds(tezosNode, keystore, delegationContractAddress, 20000, 5000000);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected an operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Completed transfer of ${conseilResult.amount}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
}
async function sendDelegatedFunds() {
console.log(`~~ sendDelegatedFunds: 500000µtz from ${delegationContractAddress} into ${anotherAccountAddress}`);
const nodeResult = await BabylonDelegationHelper.sendDelegatedFunds(tezosNode, keystore, delegationContractAddress, 20000, 500000, undefined, anotherAccountAddress);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected an operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Completed transfer of ${conseilResult.amount}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
}
async function delegationContractWithdraw() {
console.log(`~~ delegationContractWithdraw: 500000µtz from ${delegationContractAddress} into ${keystore.publicKeyHash}`);
const nodeResult = await BabylonDelegationHelper.withdrawDelegatedFunds(tezosNode, keystore, delegationContractAddress, 20000, 500000);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected an operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Completed transfer of ${conseilResult.amount}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
}
async function deployMichelineContract(): Promise<string> {
console.log(`~~ deployMichelineContract`);
const contract = `[
{ "prim":"parameter", "args":[ { "prim":"string" } ] },
{ "prim":"storage", "args":[ { "prim":"string" } ] },
{
"prim":"code",
"args":[
[
{ "prim":"CAR" },
{ "prim":"NIL", "args":[ { "prim":"operation" } ] },
{ "prim":"PAIR" }
]
]
}
]`;
const storage = '{"string": "Sample"}';
const nodeResult = await TezosNodeWriter.sendContractOriginationOperation(tezosNode, keystore, 0, undefined, 100000, '', 1000, 100000, contract, storage, TezosParameterFormat.Micheline);
const groupid = clearRPCOperationGroupHash(nodeResult['operationGroupID']);
console.log(`Injected origination operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Originated contract at ${conseilResult.originated_contracts}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
return conseilResult.originated_contracts;
}
async function deployMichelsonContract(): Promise<string> {
console.log(`~~ deployMichelsonContract`);
const contract = `parameter string;
storage string;
code { DUP;
DIP { CDR ; NIL string ; SWAP ; CONS } ;
CAR ; CONS ;
CONCAT;
NIL operation; PAIR}`;
const storage = '"Sample"';
const fee = Number((await TezosConseilClient.getFeeStatistics(conseilServer, conseilServer.network, OperationKindType.Origination))[0]['high']);
const nodeResult = await TezosNodeWriter.sendContractOriginationOperation(tezosNode, keystore, 0, undefined, fee, '', 1000, 100000, contract, storage, TezosParameterFormat.Michelson);
const groupid = clearRPCOperationGroupHash(nodeResult['operationGroupID']);
console.log(`Injected origination operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Originated contract at ${conseilResult.originated_contracts}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
return conseilResult.originated_contracts;
}
async function invokeContract(address: string, parameter: string, entrypoint: string = '') {
console.log(`~~ invokeContract`);
const fee = Number((await TezosConseilClient.getFeeStatistics(conseilServer, conseilServer.network, OperationKindType.Transaction))[0]['high']);
let storageResult = await TezosNodeReader.getContractStorage(tezosNode, address);
console.log(`initial storage: ${JSON.stringify(storageResult)}`);
const { gas, storageCost: freight } = await TezosNodeWriter.testContractInvocationOperation(tezosNode, 'main', keystore, address, 10000, fee, 1000, 100000, entrypoint, parameter, TezosParameterFormat.Michelson);
console.log(`cost: ${JSON.stringify(await TezosNodeWriter.testContractInvocationOperation(tezosNode, 'main', keystore, address, 10000, fee, 1000, 100000, entrypoint, parameter, TezosParameterFormat.Michelson))}`)
const nodeResult = await TezosNodeWriter.sendContractInvocationOperation(tezosNode, keystore, address, 10000, fee, '', freight, gas, entrypoint, parameter, TezosParameterFormat.Michelson);
const groupid = clearRPCOperationGroupHash(nodeResult.operationGroupID);
console.log(`Injected transaction(invocation) operation with ${groupid}`);
const conseilResult = await TezosConseilClient.awaitOperationConfirmation(conseilServer, conseilServer.network, groupid, 5, networkBlockTime);
console.log(`Completed invocation of ${conseilResult.destination}`);
storageResult = await TezosNodeReader.getContractStorage(tezosNode, address);
console.log(`modified storage: ${JSON.stringify(storageResult)}`);
console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
}
async function parseContract(address: string) {
const entryPoints = await TezosContractIntrospector.generateEntryPointsFromAddress(conseilServer, conseilServer.network, address);
for (const entryPoint of entryPoints) {
console.log(`${entryPoint.name}(${entryPoint.parameters.map(p => (p.name ? p.name + ': ' : '') + p.type + (p.optional ? '?' : '')).join(', ')})`)
console.log(entryPoint.structure)
}
}
async function dumpMempool(account: string) {
const rr = await TezosNodeReader.getMempoolOperationsForAccount(tezosNode, account);
await Promise.all(
rr.map(async (r) => {
const ttl = await TezosNodeReader.estimateBranchTimeout(tezosNode, r['branch']);
const t = r['contents'][0];
console.log(`operation ${r['hash']} for ${new BigNumber(t.amount || 0).toNumber()}xtz expires in ${ttl} blocks`)
})
);
}
async function run() {
// Account initialization
keystore = await initAccount(); // TODO: read/write settings
await activateAccount();
await revealAccount();
// Basic operations
await sendTransaction();
await delegatePrimaryAccount();
// Delegation operations
delegationContractAddress = await deployDelegationContract();
await depositDelegatedFunds();
await sendDelegatedFunds();
await delegationContractWithdraw();
// Basic contract operations
await deployMichelineContract();
contractAddress = await deployMichelsonContract();
await invokeContract(contractAddress, '"new text"');
}
run();
{
"name": "tezos-workflow-test",
"version": "7.0.0",
"description": "Integrated Tezos Workflow Test",
"main": "index.ts",
"scripts": {
"postinstall": "npm run build",
"build": "tsc index.ts",
"start": "node index.js"
},
"author": "anonymoussprocket",
"license": "UNLICENSED",
"engines": {
"node": ">=12.16.1",
"npm": ">=6.13.4"
},
"homepage": "https://gist.github.com/anonymoussprocket",
"dependencies": {
"bignumber.js": "9.0.0",
"conseiljs": "0.4.3-beta.1",
"glob": "^7.1.6"
},
"devDependencies": {
"typescript": "3.8.3"
}
}
@ltfschoen

This comment has been minimized.

Copy link

@ltfschoen ltfschoen commented Feb 22, 2020

If you're getting error 404 not found, then try changing your Tezos Bablylonnet Node address from https://tezos-dev.cryptonomic-infra.tech/ to https://conseil-dev.cryptonomic-infra.tech:443 in index.ts

@ltfschoen

This comment has been minimized.

Copy link

@ltfschoen ltfschoen commented Feb 22, 2020

@TriplEight

This comment has been minimized.

Copy link

@TriplEight TriplEight commented Feb 22, 2020

  1. Copy addresses from https://nautilus.cloud/ they are provided with trailing slash /.
  2. Put these adresses in https://gist.github.com/anonymoussprocket/148d82fc9bf6c413be04155a90d57be6#file-index-ts-L9-L10 and
  3. run npm install or npm run build
  4. npm start
    You'll get
ConseilDataClient.executeEntityQuery request: https://tezos-dev.cryptonomic-infra.tech//v2/data/tezos/babylonnet/accounts, {"fields":[],"predicates":[{"field":"account_id","operation":"eq","set":["tz1SKm3uu43HL1jqnedWtxsbzvwq4UGZ9JF5"],"inverse":false}],"orderBy":[],"aggregation":[],"limit":1}
ConseilDataClient.executeEntityQuery request: https://tezos-dev.cryptonomic-infra.tech//v2/data/tezos/babylonnet/accounts, {"fields":[],"predicates":[{"field":"account_id","operation":"eq","set":["tz1SKm3uu43HL1jqnedWtxsbzvwq4UGZ9JF5"],"inverse":false}],"orderBy":[],"aggregation":[],"limit":1}, failed with Not Found(404)
(node:6233) UnhandledPromiseRejectionWarning: Error
    at /home/tripleight/code/practice/tezos_workshop/node_modules/conseiljs/dist/reporting/ConseilDataClient.js:34:27
    at processTicksAndRejections (internal/process/task_queues.js:94:5)
(node:6233) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:6233) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Which fails simply because of the double slash in the address.

@ltfschoen

This comment has been minimized.

Copy link

@ltfschoen ltfschoen commented Feb 22, 2020

@TriplEight, see my previous comment, add :443 to the end too

@TriplEight

This comment has been minimized.

Copy link

@TriplEight TriplEight commented Feb 24, 2020

@TriplEight, see my previous comment, add :443 to the end too

cheers, posted my report and then saw yours.

@lijianl

This comment has been minimized.

Copy link

@lijianl lijianl commented Mar 29, 2020

faucetAccount activate failed? why
-->
https://tezos-dev.cryptonomic-infra.tech//injection/operation?chain=main
Injected activation operation with [{kind:temporary,id:failure,msg:Error while applying operation onyhAXdQsbnHqu4Q7S4mUi3nGLZxGWKS7b2HzWEiFZXV3cijLzY:\nrefused (Error:\n Invalid activation. The public key tz1SamNpDf5WU6cXs8zWhAWzkWn9omqyJM2z does not match any commitment.\n)}]

@lijianl

This comment has been minimized.

Copy link

@lijianl lijianl commented Mar 29, 2020

where could I find a activate code for an empty Tezos account?

@lijianl

This comment has been minimized.

Copy link

@lijianl lijianl commented Mar 29, 2020

async function createAccount() {
const mnemonic = conseiljs.TezosWalletUtil.generateMnemonic();
console.log(mnemonic: ${mnemonic});
const keystore = await conseiljs.TezosWalletUtil.unlockIdentityWithMnemonic(mnemonic, '');
console.log(account id: ${keystore.publicKeyHash});
console.log(public key: ${keystore.publicKey});
console.log(secret key: ${keystore.privateKey});
}

how to activate a new empty account, what is the activate code?

@anonymoussprocket

This comment has been minimized.

Copy link
Owner Author

@anonymoussprocket anonymoussprocket commented Mar 29, 2020

get a faucet file from faucet.tzalpha.net

@lijianl

This comment has been minimized.

Copy link

@lijianl lijianl commented Mar 30, 2020

where to get a fundraiser account on the main net, send a transaction from a Mnemonic account is always failed??
could you help with this

@anonymoussprocket

This comment has been minimized.

Copy link
Owner Author

@anonymoussprocket anonymoussprocket commented Mar 30, 2020

Please join our developer room on Riot

@lijianl

This comment has been minimized.

Copy link

@lijianl lijianl commented Mar 30, 2020

i use TezosWalletUtil.getKeysFromMnemonicAndPassphrase to get a fundraiser account, but how to get an activate code

@lijianl

This comment has been minimized.

Copy link

@lijianl lijianl commented Mar 30, 2020

developer room on Riot has an error, "Invalid homeserver discovery response" could you help to solve above issue

@lijianl

This comment has been minimized.

Copy link

@lijianl lijianl commented Mar 30, 2020

Please join our developer room on Riot

the URL is ERROR:Invalid homeserver discovery response

@lijianl

This comment has been minimized.

Copy link

@lijianl lijianl commented Mar 30, 2020

Please join our developer room on Riot

the URL is ERROR:Invalid homeserver discovery response

could you give me any help with this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.