diff --git a/agent/base.ts b/agent/base.ts index 0aab708fc93..1a72d8c973d 100644 --- a/agent/base.ts +++ b/agent/base.ts @@ -1,7 +1,7 @@ /** Fadroma. Copyright (C) 2023 Hack.bg. License: GNU AGPLv3 or custom. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . **/ -import { Error as BaseError } from '@hackbg/oops' +import { Error } from '@hackbg/oops' import { Console, bold, colors } from '@hackbg/logs' import type { Deployment } from './deploy' @@ -79,22 +79,9 @@ export type Address = string export type TxHash = string /** Error kinds. */ -class FadromaError extends BaseError { - /** Thrown when a required parameter is missing. */ - static Missing: typeof FadromaError_Missing -} +class FadromaError extends Error {} -class FadromaError_Missing extends FadromaError.define( - 'Missing', (msg='a required parameter was missing') => msg as string -) { - static Address = this.define('Address', () => 'no address') - static Name = this.define("Name", () => "no name") - static Uploader = this.define('Uploader', () => "no uploader") - static Workspace = this.define('Workspace', () => "no workspace") +export { + Console, + FadromaError as Error } - -export const Error = Object.assign(FadromaError, { - Missing: FadromaError_Missing, -}) - -export { Console } diff --git a/agent/chain.ts b/agent/chain.ts index f7311b96a96..e9d495137c1 100644 --- a/agent/chain.ts +++ b/agent/chain.ts @@ -292,16 +292,16 @@ export abstract class Agent { contract = new UploadedCode({ codeId: contract }) } if (isNaN(Number(contract.codeId))) { - throw new Error(`invalid code id: ${contract.codeId}`) + throw new Error(`can't instantiate contract with missing code id: ${contract.codeId}`) } if (!contract.codeId) { - throw new Error.Missing.CodeId() + throw new Error("can't instantiate contract without code id") } if (!options.label) { - throw new Error.Missing.Label() + throw new Error("can't instantiate contract without label") } if (!options.initMsg) { - throw new Error.Missing.InitMsg() + throw new Error("can't instantiate contract without init message") } const t0 = performance.now() const result = await this.doInstantiate(contract.codeId, { @@ -363,7 +363,7 @@ export abstract class Agent { abstract getCodeHashOfCodeId (codeId: CodeId): Promise - abstract doQuery (contract: { address: Address }, message: Message): + protected abstract doQuery (contract: { address: Address }, message: Message): Promise /** Send native tokens to 1 recipient. */ diff --git a/agent/client.ts b/agent/client.ts index bd55b11594d..2b89bbd8523 100644 --- a/agent/client.ts +++ b/agent/client.ts @@ -31,10 +31,10 @@ export class ContractClient { /** Execute a query on the specified contract as the specified Agent. */ query (message: Message): Promise { if (!this.agent) { - throw new Error.Missing.Agent(this.constructor?.name) + throw new Error("can't query contract without agent") } if (!this.contract.address) { - throw new Error.Missing.Address() + throw new Error("can't query contract without address") } return this.agent.query( this.contract as ContractInstance & { address: Address }, message @@ -44,10 +44,13 @@ export class ContractClient { /** Execute a transaction on the specified contract as the specified Agent. */ execute (message: Message, options: Parameters[2] = {}): Promise { if (!this.agent) { - throw new Error.Missing.Agent(this.constructor?.name) + throw new Error("can't transact with contract without agent") + } + if (!this.agent.execute) { + throw new Error("can't transact with contract without authorizing the agent") } if (!this.contract.address) { - throw new Error.Missing.Address() + throw new Error("can't transact with contract without address") } return this.agent.execute( this.contract as ContractInstance & { address: Address }, message, options diff --git a/connect/connect.ts b/connect/connect.ts index 8e649341895..9e721e9ede3 100644 --- a/connect/connect.ts +++ b/connect/connect.ts @@ -43,7 +43,7 @@ export const connectModes = { // Support for OKP4: OKP4Testnet: CW.OKP4.testnet, - OKP4Devnet: (...args: Parameters): CW.OKP4.Chain => { + OKP4Devnet: (...args: Parameters): CW.OKP4.Agent => { throw new Error('Devnets are only available through @hackbg/fadroma') }, diff --git a/connect/cw/cw-base.ts b/connect/cw/cw-base.ts index 5d3352c190c..a85d209c7ad 100644 --- a/connect/cw/cw-base.ts +++ b/connect/cw/cw-base.ts @@ -132,7 +132,12 @@ class CWAgent extends Agent { } protected async doUpload (data: Uint8Array): Promise> { - if (!this.address) throw new Error.Missing.Address() + if (!this.address) { + throw new CWError("can't upload contract without sender address") + } + if (!(this.api as SigningCosmWasmClient)?.instantiate) { + throw new CWError("can't upload contract without authorizing the agent") + } const result = await this.api.upload( this.address, data, this.fees?.upload || 'auto', "Uploaded by Fadroma" ) @@ -151,12 +156,6 @@ class CWAgent extends Agent { codeId: CodeId, options: Parameters[1] ): Promise> { - if (!options.label) { - throw new CWError("can't instantiate contract without label") - } - if (!options.initMsg) { - throw new CWError("can't instantiate contract without init message") - } if (!this.address) { throw new CWError("can't instantiate contract without sender address") } @@ -167,7 +166,7 @@ class CWAgent extends Agent { this.address!, Number(codeId), options.initMsg, - options.label, + options.label!, options.initFee || 'auto', { admin: this.address, funds: options.initSend, memo: options.initMemo } ) diff --git a/connect/cw/okp4/okp4.ts b/connect/cw/okp4/okp4.ts index e9e47c3fc96..2085a8298e1 100644 --- a/connect/cw/okp4/okp4.ts +++ b/connect/cw/okp4/okp4.ts @@ -82,7 +82,6 @@ class OKP4Agent extends Agent { /** Get clients for all Cognitarium instances, * keyed by address. */ async cognitaria ({ map = true } = {}) { - const { api } = await this.ready const ids = Object.values(cognitariumCodeIds) return await this.getContractsById(Cognitarium, ids, map) } @@ -90,7 +89,6 @@ class OKP4Agent extends Agent { /** Get clients for all Objectarium instances, * keyed by address. */ async objectaria ({ map = true } = {}) { - const { api } = await this.ready const ids = Object.values(objectariumCodeIds) return await this.getContractsById(Objectarium, ids, map) } @@ -98,7 +96,6 @@ class OKP4Agent extends Agent { /** Get clients for all Law Stone instances, * keyed by address. */ async lawStones ({ map = true } = {}) { - const { api } = await this.ready const ids = Object.values(lawStoneCodeIds) return await this.getContractsById(LawStone, ids, map) } @@ -110,14 +107,13 @@ class OKP4Agent extends Agent { ): Promise< typeof map extends true ? Map : Record > { - const { api } = await this.ready const chainId = this.chainId const contracts = map ? new Map() : {} for (const id of ids) { const codeId = Number(id) if (isNaN(codeId)) throw new Error('non-number code ID encountered') - const { checksum: codeHash } = await api.getCodeDetails(codeId) - const addresses = await api.getContracts(codeId) + const { checksum: codeHash } = await this.api.getCodeDetails(codeId) + const addresses = await this.api.getContracts(codeId) for (const address of addresses) { const contract = new Client( { address, codeHash, chainId, codeId: String(codeId) }, diff --git a/connect/scrt/scrt-chain.ts b/connect/scrt/scrt-chain.ts index ae2149e95f4..899f8ae5830 100644 --- a/connect/scrt/scrt-chain.ts +++ b/connect/scrt/scrt-chain.ts @@ -329,7 +329,9 @@ class ScrtAgent extends Agent { } async encrypt (codeHash: CodeHash, msg: Message) { - if (!codeHash) throw new Error.Missing.CodeHash() + if (!codeHash) { + throw new Error("can't encrypt message without code hash") + } const { encryptionUtils } = this.api as any const encrypted = await encryptionUtils.encrypt(codeHash, msg as object) return base64.encode(encrypted) diff --git a/connect/scrt/scrt-mocknet.ts b/connect/scrt/scrt-mocknet.ts index d213b3f4d20..af963709801 100644 --- a/connect/scrt/scrt-mocknet.ts +++ b/connect/scrt/scrt-mocknet.ts @@ -556,8 +556,12 @@ export class MocknetContract { const height = Math.floor(now/5000) const time = Math.floor(now/1000) const sent_funds: any[] = [] - if (!this.address) throw new Error.Missing.Address() - if (!this.codeHash) throw new Error.Missing.CodeHash() + if (!this.address) { + throw new Error("can't run contract without address") + } + if (!this.codeHash) { + throw new Error("can't run contract without code hash") + } const { address, codeHash } = this if (this.cwVersion === '0.x') { const block = { height, time, chain_id } diff --git a/ops/build.ts b/ops/build.ts index 92654befc83..9b50884d337 100644 --- a/ops/build.ts +++ b/ops/build.ts @@ -133,7 +133,9 @@ export abstract class LocalRustCompiler extends Compiler { protected resolveSource (source: string|Partial): Partial { if (typeof source === 'string') source = { crate: source } let { crate, workspace = this.workspace, revision = 'HEAD' } = source - if (!crate) throw new Error.Missing.Crate() + if (!crate) { + throw new Error("missing crate name") + } // If the `crate` field contains a slash, this is a crate path and not a crate name. // Add the crate path to the workspace path, and set the real crate name. if (source.crate && source.crate.includes(sep)) { @@ -581,7 +583,9 @@ export class RawLocalRustCompiler extends LocalRustCompiler { source.workspace ??= this.workspace source.revision ??= HEAD const { workspace, revision, crate } = source - if (!crate && !workspace) throw new Error.Missing.Crate() + if (!(crate || workspace)) { + throw new Error("missing crate name or workspace path") + } const { env, tmpGit, tmpBuild } = this.getEnvAndTemp(source, workspace, revision) // Run the build script as a subprocess const location = await this.runBuild(source, env) diff --git a/ops/devnets.ts b/ops/devnets.ts index ddcd57a8644..67c09928aed 100644 --- a/ops/devnets.ts +++ b/ops/devnets.ts @@ -340,8 +340,12 @@ export class Devnet implements DevnetHandle { this.log.debug('Creating...') // ensure we have image and chain id const image = await this.image - if (!this.image) throw new DevnetError.Missing.DevnetImage() - if (!this.chainId) throw new DevnetError.Missing.ChainId() + if (!this.image) { + throw new DevnetError("missing devnet container image") + } + if (!this.chainId) { + throw new DevnetError("can't create devnet without chain ID") + } // if port is unspecified or taken, increment this.port = await ports.getFreePort(this.port) // create container diff --git a/ops/project.ts b/ops/project.ts index 5c57fb2fef1..166ece2238b 100644 --- a/ops/project.ts +++ b/ops/project.ts @@ -319,7 +319,9 @@ export class Project extends CommandContext { 'export', `export current deployment to JSON`, async (path?: string) => { const deployment = await this.selectDeployment() - if (!deployment) throw new Error.Missing.Deployment() + if (!deployment) { + throw new Error("deployment not found") + } if (!path) path = process.cwd() // If passed a directory, generate file name let file = $(path) @@ -344,12 +346,12 @@ export class Project extends CommandContext { contracts: Record> = {}, ): InstanceType { if (!name) { - throw new Error.Missing.Name() + throw new Error("missing deployment name") } if (this.deployStore.has(name)) { return this.Deployment.fromReceipt(this.deployStore.get(name)!) } else { - throw new Error.Missing.Deployment() + throw new Error(`deployment not found: ${name}`) } } diff --git a/ops/stores.ts b/ops/stores.ts index 1d23f6984af..d6aa5d37b6a 100644 --- a/ops/stores.ts +++ b/ops/stores.ts @@ -37,8 +37,12 @@ export class JSONFileUploadStore extends UploadStore { } get (codeHash: CodeHash|{ codeHash: CodeHash }): UploadedCode|undefined { - if (typeof codeHash === 'object') codeHash = codeHash.codeHash - if (!codeHash) throw new Error.Missing.CodeHash() + if (typeof codeHash === 'object') { + codeHash = codeHash.codeHash + } + if (!codeHash) { + throw new Error("can't get upload info: missing code hash") + } const receipt = this.dir.at(`${codeHash!.toLowerCase()}.json`).as(JSONFile) if (receipt.exists()) { const uploaded = receipt.load() @@ -53,8 +57,12 @@ export class JSONFileUploadStore extends UploadStore { } set (codeHash: CodeHash|{ codeHash: CodeHash }, value: Partial): this { - if (typeof codeHash === 'object') codeHash = codeHash.codeHash - if (!codeHash) throw new Error.Missing.CodeHash() + if (typeof codeHash === 'object') { + codeHash = codeHash.codeHash + } + if (!codeHash) { + throw new Error("can't set upload info: missing code hash") + } const receipt = this.dir.at(`${codeHash.toLowerCase()}.json`).as(JSONFile) this.log('writing', receipt.shortPath) receipt.save(super.get(codeHash)!.toReceipt())