|
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(); |