diff --git a/src/accounts/accountManager.ts b/src/accounts/accountManager.ts index 5c9fe48..609bb67 100644 --- a/src/accounts/accountManager.ts +++ b/src/accounts/accountManager.ts @@ -1,4 +1,5 @@ import { Api } from 'eosjs'; +import { convertLegacyPublicKey } from 'eosjs/dist/eosjs-numeric'; import * as ecc from 'eosjs-ecc'; import { Account } from './account'; @@ -11,6 +12,12 @@ interface AccountCreationOptions { } export class AccountManager { + static createAccount = async (options?: AccountCreationOptions) => { + const [account] = await AccountManager.createAccounts(1, options); + + return account; + }; + static createAccounts = async (numberOfAccounts = 1, options?: AccountCreationOptions) => { const accounts = []; for (let i = 0; i < numberOfAccounts; i++) { @@ -39,9 +46,13 @@ export class AccountManager { if (!account.privateKey) throw new Error('Missing private key.'); if (EOSManager.signatureProvider) { - EOSManager.signatureProvider.keys.set(account.publicKey, account.privateKey); + const nonLegacyPublicKey = convertLegacyPublicKey(account.publicKey); + EOSManager.signatureProvider.keys.set(nonLegacyPublicKey, account.privateKey); + EOSManager.signatureProvider.availableKeys.push(nonLegacyPublicKey); } + console.log('Signature provider', EOSManager.signatureProvider); + const systemContract = await eos.getContract('eosio'); const actions: any = [ @@ -78,7 +89,9 @@ export class AccountManager { }, ]; - // Do we need to buyrambytes? If there's a system contract with that action we do. + // Note: You can deploy the system without system contracts. In this scenario, + // newaccount alone is enough. If there is a system contract with that action, + // then we definitely need to do it though. if (systemContract.actions.has('buyrambytes')) { actions.push({ account: 'eosio', diff --git a/src/cli/utils.ts b/src/cli/utils.ts index d85edef..a423908 100644 --- a/src/cli/utils.ts +++ b/src/cli/utils.ts @@ -163,11 +163,6 @@ export const runTests = async () => { mocha.addFile(path.join(workingDirectory, testFile)); } - // Now we're all ready to go. Let's make sure we have enough blocks that our tests will - // work out of the box. - console.log('Waiting for block #4.'); - await untilBlockNumber(4); - // Run the tests. mocha.run(failures => { process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures diff --git a/src/contracts/contract.ts b/src/contracts/contract.ts new file mode 100644 index 0000000..5f71327 --- /dev/null +++ b/src/contracts/contract.ts @@ -0,0 +1 @@ +export class Contract {} diff --git a/src/contracts/contractDeployer.ts b/src/contracts/contractDeployer.ts new file mode 100644 index 0000000..922604a --- /dev/null +++ b/src/contracts/contractDeployer.ts @@ -0,0 +1,106 @@ +import * as path from 'path'; +import { readFile as readFileCallback } from 'fs'; +import { promisify } from 'util'; +import { Serialize } from 'eosjs'; + +const readFile = promisify(readFileCallback); + +import { Contract } from './contract'; +import { Account, AccountManager } from '../accounts'; +import { EOSManager } from '../eosManager'; + +export class ContractDeployer { + public static async deployAtName( + account: Account, + contractIdentifier: string + ) { + console.log('Available keys', await EOSManager.signatureProvider.getAvailableKeys()); + + const buffer = new Serialize.SerialBuffer({ + textEncoder: EOSManager.api.textEncoder, + textDecoder: EOSManager.api.textDecoder, + }); + + const abiPath = path.join('.lamington', 'compiled_contracts', `${contractIdentifier}.abi`); + const wasmPath = path.join('.lamington', 'compiled_contracts', `${contractIdentifier}.wasm`); + + let abi = JSON.parse(await readFile(abiPath, 'utf8')); + const wasm = await readFile(wasmPath); + + const abiDefinition = EOSManager.api.abiTypes.get(`abi_def`); + + // We need to make sure the abi has every field in abiDefinition.fields + // otherwise serialize throws + if (!abiDefinition) + throw new Error('Could not retrieve abiDefinition from EOS API when flattening ABIs.'); + + abi = abiDefinition.fields.reduce( + (acc, { name: fieldName }) => Object.assign(acc, { [fieldName]: acc[fieldName] || [] }), + abi + ); + abiDefinition.serialize(buffer, abi); + + const requiredKeys = await EOSManager.rpc.getRequiredKeys({ + transaction: { + actions: [ + { + account: `eosio`, + name: `setcode`, + authorization: account.active, + data: { + account: account.name, + vmtype: 0, + vmversion: 0, + code: wasm.toString(`hex`), + }, + }, + { + account: `eosio`, + name: `setabi`, + authorization: account.active, + data: { + account: account.name, + abi: Buffer.from(buffer.asUint8Array()).toString(`hex`), + }, + }, + ], + }, + availableKeys: await EOSManager.signatureProvider.getAvailableKeys(), + }); + + console.log('Required keys', requiredKeys); + + await EOSManager.transact({ + actions: [ + { + account: `eosio`, + name: `setcode`, + authorization: account.active, + data: { + account: account.name, + vmtype: 0, + vmversion: 0, + code: wasm.toString(`hex`), + }, + }, + { + account: `eosio`, + name: `setabi`, + authorization: account.active, + data: { + account: account.name, + abi: Buffer.from(buffer.asUint8Array()).toString(`hex`), + }, + }, + ], + }); + + return new Contract(); + } + + public static async deployClean(contractIdentifier: string) { + const account = await AccountManager.createAccount(); + + return await ContractDeployer.deployAtName(account, contractIdentifier); + } +} diff --git a/src/contracts/index.ts b/src/contracts/index.ts new file mode 100644 index 0000000..778ce74 --- /dev/null +++ b/src/contracts/index.ts @@ -0,0 +1,2 @@ +export * from './contract'; +export * from './contractDeployer'; diff --git a/src/index.ts b/src/index.ts index c9b8583..111f029 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export * from './accounts'; +export * from './contracts'; import * as cliUtils from './cli/utils'; export const CLI = cliUtils; diff --git a/src/utils.ts b/src/utils.ts index 2ece5a2..7cb9fbb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -13,3 +13,5 @@ export const untilBlockNumber = async (number: number) => { export const sleep = async (delayInMs: number) => new Promise(resolve => setTimeout(resolve, delayInMs)); + +export const nextBlock = () => sleep(500);