Repository: defi-wonderland/smock (ts)

Issue:
Create fakes with Contract Interface
**Is your feature request related to a problem? Please describe.**
I often would like to test contracts that depend on external/3rd party contracts. I'd like to only import the interfaces for those contracts into my repo/codebase since they often times are implemented with a different solidity version than the one i'm using. Therefore in my project i'll have my contracts (their sourcecode) and interfaces for external contracts I use.

I try to create "fake" implementations of those external contracts using smock but am unable to, I need to source code for them.

**Describe the solution you'd like**
Being able to use `smock.fake()` with a contract interface.

**Describe alternatives you've considered**
I can stub out functionality for those contract interfaces, just have empty functions that do nothing and then use `smock.fake` but that's a hassle.

**Additional context**
I've tried using code like:
```
// IBasicController is an interface
await smock.fake("IBasicController");
```
and i get the error:
```
NomicLabsHardhatPluginError: You are trying to create a contract factory for the contract IBasicController, which is abstract and can't be deployed.
If you want to call a contract using IBasicController as its interface use the "getContractAt" function instead.
```


Pull request description:
feat: support fakes from interfaces
**Description**
Slightly modifies the spec generation process so that we can successfully generate fakes from interfaces.

**Metadata**
- Fixes #77


Patch:
diff --git a/src/factories/ethers-interface.ts b/src/factories/ethers-interface.ts
index 16e1b0d..abb387a 100644
--- a/src/factories/ethers-interface.ts
+++ b/src/factories/ethers-interface.ts
@@ -6,9 +6,17 @@ export async function ethersInterfaceFromSpec(spec: FakeContractSpec): Promise<e
   if (typeof spec === 'string') {
     try {
       return new ethers.utils.Interface(spec);
-    } catch {
+    } catch {}
+
+    try {
       return (await (hre as any).ethers.getContractFactory(spec)).interface;
-    }
+    } catch {}
+
+    try {
+      return (await (hre as any).ethers.getContractAt(spec, ethers.constants.AddressZero)).interface;
+    } catch {}
+
+    throw new Error(`unable to generate smock spec from string`);
   }
 
   let foundInterface: any = spec;


Test patch:
diff --git a/test/contracts/watchable-function-logic/IPartialReceiver.sol b/test/contracts/watchable-function-logic/IPartialReceiver.sol
new file mode 100644
index 0000000..8163cda
--- /dev/null
+++ b/test/contracts/watchable-function-logic/IPartialReceiver.sol
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+interface IPartialReceiver {
+  function receiveEmpty() external;
+}
diff --git a/test/unit/fake/initialization.spec.ts b/test/unit/fake/initialization.spec.ts
index a32d45c..db16693 100644
--- a/test/unit/fake/initialization.spec.ts
+++ b/test/unit/fake/initialization.spec.ts
@@ -76,4 +76,9 @@ describe('Fake: Initialization', () => {
     const receiver2 = await smock.fake<Returner>('Returner');
     await expect(receiver2.callStatic.getBoolean()).not.to.be.reverted;
   });
+
+  it('should work for an interface', async () => {
+    fake = await smock.fake('IPartialReceiver');
+    expect(fake.receiveEmpty._watchable).not.to.be.undefined;
+  });
 });