diff --git a/src/accounts/account.ts b/src/accounts/account.ts index ce2bcb9..1371d10 100644 --- a/src/accounts/account.ts +++ b/src/accounts/account.ts @@ -7,25 +7,28 @@ export class Account { /** EOSIO account name */ public name: string; /** EOSIO account public key */ - public publicKey: string; + public publicKey?: string; /** EOSIO account private key */ - public privateKey: string; + public privateKey?: string; /** EOSIO account permissions */ public permissions: Permissions; - constructor(name: string, privateKey: string, publicKey?: string) { + constructor(name: string, privateKey?: string, publicKey?: string) { // Store references this.name = name; - this.privateKey = privateKey; - this.publicKey = ecc.privateToPublic(privateKey); + if (privateKey) { + this.privateKey = privateKey; - if (publicKey && publicKey !== this.publicKey) { - throw new Error( - `Supplied public key does not match private key. Supplied key: ${publicKey} Expected key: ${ecc.privateToPublic( - privateKey - )} This is usually caused by using the legacy key format vs the new style key format.` - ); + this.publicKey = ecc.privateToPublic(privateKey); + + if (publicKey && publicKey !== this.publicKey) { + throw new Error( + `Supplied public key does not match private key. Supplied key: ${publicKey} Expected key: ${ecc.privateToPublic( + privateKey + )} This is usually caused by using the legacy key format vs the new style key format.` + ); + } } // Set default permissions diff --git a/src/contracts/contract.ts b/src/contracts/contract.ts index 745cb64..c7fd469 100644 --- a/src/contracts/contract.ts +++ b/src/contracts/contract.ts @@ -14,6 +14,15 @@ export interface ContractActionOptions { from?: Account; } +export interface ContractConstructorArgs { + eos: Api; + identifier?: string; + account: Account; + abi: Abi; + actions: Map; + types: Map; +} + /** * Adds additional functionality to the EOSJS `Contract` class */ @@ -22,8 +31,9 @@ export class Contract implements EOSJSContract { private _eos: Api; /** @hidden Current contract account */ private _account: Account; - /** @hidden Contract identifier. Typically the contract file name minus the extension */ - private _identifier: string; + /** @hidden Contract identifier. Typically the contract file name minus the extension. + * Can be undefined when the contract is loaded as already deployed and we're never given an indentifier to map it back to. */ + private _identifier?: string; /** @hidden Current contract ABI */ private _abi: Abi; /** Deployed contract actions */ @@ -36,7 +46,7 @@ export class Contract implements EOSJSContract { * @author Kevin Brown * @returns Current contract account */ - public get account(): Account { + public get account() { return this._account; } @@ -45,18 +55,11 @@ export class Contract implements EOSJSContract { * @author Kevin Brown * @returns Contract identifier */ - public get identifier(): string { + public get identifier() { return this._identifier; } - constructor( - eos: Api, - identifier: string, - account: Account, - abi: Abi, - actions: Map, - types: Map - ) { + constructor({ eos, identifier, account, abi, actions, types }: ContractConstructorArgs) { // Store contract arguments this._eos = eos; this._identifier = identifier; diff --git a/src/contracts/contractDeployer.ts b/src/contracts/contractDeployer.ts index 627031a..b52663b 100644 --- a/src/contracts/contractDeployer.ts +++ b/src/contracts/contractDeployer.ts @@ -10,6 +10,7 @@ import { Contract } from './contract'; import { Account, AccountManager } from '../accounts'; import { EOSManager } from '../eosManager'; import { ConfigManager } from '../configManager'; +import { ContractLoader } from './contractLoader'; /** * Provides a set of methods to manage contract deployment @@ -89,10 +90,8 @@ export class ContractDeployer { }, ], }); - // Fetch the contract actions and types - const { actions, types } = await EOSManager.api.getContract(account.name); - // Return our newly deployed contract instance - return new Contract(EOSManager.api, contractIdentifier, account, abi, actions, types) as T; + + return await ContractLoader.at(account); } /** @@ -122,7 +121,7 @@ export class ContractDeployer { * ContractDeployer.deployWithName('mycontract', 'mycontractname'); * ``` * - * @note Generating a random private key is not safe in the cryptographic sense. It can be used for testing. + * @note Generating a pseudorandom private key is not safe in the cryptographic sense. It can be used for testing. * @author Mitch Pierias * @param contractIdentifier Contract identifier, typically the contract filename minus the extension * @param accountName Account name @@ -137,8 +136,16 @@ export class ContractDeployer { // Initialize account with name const account = new Account(accountName, privateKey); + + console.log('Created account:'); + console.log(`Name: ${accountName}`); + console.log(`Private Key: ${privateKey}`); + + console.log('Setting up account'); await AccountManager.setupAccount(account); + console.log('Success'); + // Call the deployToAccount method with the account return await ContractDeployer.deployToAccount(contractIdentifier, account); } diff --git a/src/contracts/contractLoader.ts b/src/contracts/contractLoader.ts new file mode 100644 index 0000000..7dfe331 --- /dev/null +++ b/src/contracts/contractLoader.ts @@ -0,0 +1,37 @@ +import { Contract } from './contract'; +import { Account } from '../accounts'; +import { EOSManager } from '../eosManager'; + +/** + * Provides a set of methods to create contract references for already existing contracts + */ +export class ContractLoader { + /** + * Loads a contract instance for a contract which is already deployed to the blockchain. + * + * ```typescript + * ContractLoader.at('mycontract'); + * ``` + * @author Kevin Brown + * @param accountName The account name where the contract is already deployed. + * @returns Contract instance + */ + public static async at(account: Account) { + // Load the ABI from the blockchain. + const { abi } = await EOSManager.rpc.get_abi(account.name); + + if (!abi) throw new Error(`Could not load ABI for contract at '${account.name}'.`); + + // Fetch the contract actions and types + const { actions, types } = await EOSManager.api.getContract(account.name); + + // Return our newly deployed contract instance + return new Contract({ + eos: EOSManager.api, + account, + abi, + actions, + types, + }) as T; + } +} diff --git a/src/contracts/index.ts b/src/contracts/index.ts index 3ee99fa..55d0474 100644 --- a/src/contracts/index.ts +++ b/src/contracts/index.ts @@ -1,4 +1,5 @@ export * from './contract'; export * from './contractDeployer'; +export * from './contractLoader'; export * from './tableRowsResult'; export * from './typeGenerator'; diff --git a/src/eosManager.ts b/src/eosManager.ts index 9c29356..b34675a 100644 --- a/src/eosManager.ts +++ b/src/eosManager.ts @@ -5,6 +5,12 @@ import { JsSignatureProvider } from 'eosjs/dist/eosjs-jssig'; import { Account } from './accounts'; import { ConfigManager } from './configManager'; +interface InitArgs { + adminAccount: Account; + chainId?: string; + httpEndpoint: string; +} + /** * Manages client connection and communication with a local EOSIO node */ @@ -25,14 +31,36 @@ export class EOSManager { */ static initWithDefaults = () => { // Create eosio account and configure signature provider - const adminPrivateKey = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'; - EOSManager.adminAccount = new Account('eosio', adminPrivateKey); - EOSManager.signatureProvider = new JsSignatureProvider([adminPrivateKey]); + // NOTE: This is a known EOS development key used in the EOS docs. It is + // UNSAFE to use this key on any public network. + const adminAccount = new Account( + 'eosio', + '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3' + ); + + EOSManager.init({ httpEndpoint: 'http://127.0.0.1:8888', adminAccount }); + }; + /** + * Initializes a connection to any EOSIO node and sets the administration keys which + * Lamington uses to deploy contracts, create accounts, etc. + * @author Kevin Brown + * @example + */ + static init = ({ httpEndpoint, adminAccount, chainId }: InitArgs) => { + // Create eosio account and configure signature provider + EOSManager.adminAccount = adminAccount; + + // If we have a key to sign with, go ahead and hook it up. + if (adminAccount.privateKey) { + EOSManager.signatureProvider = new JsSignatureProvider([adminAccount.privateKey]); + } + // Typecasting as any here to prevent a problem with the types disagreeing for fetch, // when this is actually following the getting started docs on EOSJS. - EOSManager.rpc = new JsonRpc('http://127.0.0.1:8888', { fetch: fetch as any }); + EOSManager.rpc = new JsonRpc(httpEndpoint, { fetch: fetch as any }); EOSManager.api = new Api({ rpc: EOSManager.rpc, + chainId, signatureProvider: EOSManager.signatureProvider, // Same deal here, type mismatch when there really shouldn't be. textDecoder: new TextDecoder() as any,