Skip to content

Commit

Permalink
refactor!: deprecate static / non-static supportsInterface methods
Browse files Browse the repository at this point in the history
  • Loading branch information
CJ42 committed Oct 17, 2023
1 parent 4ab134b commit a164967
Show file tree
Hide file tree
Showing 6 changed files with 2 additions and 271 deletions.
79 changes: 0 additions & 79 deletions docs/classes/ERC725.md
Original file line number Diff line number Diff line change
Expand Up @@ -1396,82 +1396,3 @@ await myErc725.isValidSignature(
[lsp6 keymanager standard]: https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager
[lsp-2 erc725yjsonschema]: https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-2-ERC725YJSONSchema.md

## supportsInterface

```js
myERC725.supportsInterface(interfaceIdOrName);
```

```js
ERC725.supportsInterface(interfaceIdOrName, options);
```

You can use this function if you need to check if the ERC725 object or a smart contract supports a specific interface (by ID or name). When you use the function on your instantiated ERC725 class, it will use the contract address and provider provided at instantiation. On non instantiated class, you need to specify them in the `options` parameter.

:::caution
The `interfaceId` is not the most secure way to check for a standard, as they could be set manually.
:::

#### Parameters

##### 1. `interfaceIdOrName` - String

Either a string of the hexadecimal `interfaceID` as defined by [ERC165](https://eips.ethereum.org/EIPS/eip-165) or one of the predefined interface names:

| interfaceName | Standard |
| :------------------------------ | :------------------------------------------------------------------------------------------------------------------------- |
| `ERC1271` | [EIP-1271: Standard Signature Validation Method for Contracts](https://eips.ethereum.org/EIPS/eip-1271) |
| `ERC725X` | [EIP-725: General execution standard](https://eips.ethereum.org/EIPS/eip-725) |
| `ERC725Y` | [EIP-725: General key-value store](https://eips.ethereum.org/EIPS/eip-725) |
| `LSP0ERC725Account` | [LSP-0: ERC725 Account](https://docs.lukso.tech/standards/universal-profile/lsp0-erc725account) |
| `LSP1UniversalReceiver` | [LSP-1: Universal Receiver](https://docs.lukso.tech/standards/generic-standards/lsp1-universal-receiver) |
| `LSP1UniversalReceiverDelegate` | [LSP-1: Universal Receiver Delegate](https://docs.lukso.tech/standards/universal-profile/lsp1-universal-receiver-delegate) |
| `LSP6KeyManager` | [LSP-6: Key Manager](https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager) |
| `LSP7DigitalAsset` | [LSP-7: Digital Asset](https://docs.lukso.tech/standards/nft-2.0/LSP7-Digital-Asset) |
| `LSP8IdentifiableDigitalAsset` | [LSP-8: Identifiable Digital Asset](https://docs.lukso.tech/standards/nft-2.0/LSP8-Identifiable-Digital-Asset) |
| `LSP9Vault` | [LSP-9: Vault](https://docs.lukso.tech/standards/universal-profile/lsp9-vault) |

:::info

The `interfaceName` will only check for the latest version of the standard's `interfaceID`, which can be found in `src/constants/interfaces`. For LSPs, the `interfaceIDs` are taken from the latest release of the [@lukso/lsp-smart-contracts](https://github.com/lukso-network/lsp-smart-contracts) library.

:::info

##### 2. `options` - Object (optional)

On non instantiated class, you should provide an `options` object.

| Name | Type | Description |
| :-------- | :----- | :------------------------------------------------------------------- |
| `address` | string | Address of the smart contract to check against a certain interface. |
| `rpcUrl` | string | RPC URL to connect to the network the smart contract is deployed to. |

#### Returns

| Type | Description |
| :----------------- | :------------------------------------------------------------ |
| `Promise<boolean>` | Returns `true` if the interface was found, otherwise `false`. |

#### Examples

```javascript title="By using the interface ID"
myErc725.supportsInterface('0xfd4d5c50');
// true

ERC725.supportsInterface('0xfd4d5c50', {
address: '0xe408BDDbBAB1985006A2c481700DD473F932e5cB',
rpcUrl: 'https://rpc.testnet.lukso.network',
});
// false
```

```javascript title="By using interface name"
myErc725.supportsInterface('LSP0ERC725Account');
// false

ERC725.supportsInterface('LSP0ERC725Account', {
address: '0x0Dc07C77985fE31996Ed612F568eb441afe5768D',
rpcUrl: 'https://rpc.testnet.lukso.network',
});
// true
```
36 changes: 0 additions & 36 deletions src/constants/interfaces.ts

This file was deleted.

39 changes: 0 additions & 39 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import {
SUPPORTED_HASH_FUNCTION_STRINGS,
} from './constants/constants';
import { decodeKey } from './lib/decodeData';
import { INTERFACE_IDS_0_10_2 } from './constants/interfaces';

const address = '0x0c03fba782b07bcf810deb3b7f0595024a444f4e';

Expand Down Expand Up @@ -1376,44 +1375,6 @@ describe('encodeKeyName', () => {
});
});

describe('supportsInterface', () => {
const erc725Instance = new ERC725([]);

it('is available on instance and class', () => {
chaiAssert.typeOf(ERC725.supportsInterface, 'function');
chaiAssert.typeOf(erc725Instance.supportsInterface, 'function');
});

const interfaceId = INTERFACE_IDS_0_10_2.LSP1UniversalReceiver;
const rpcUrl = 'https://my.test.provider';
const contractAddress = '0xcafecafecafecafecafecafecafecafecafecafe';

it('should throw when provided address is not an address', async () => {
try {
await ERC725.supportsInterface(interfaceId, {
address: 'notAnAddress',
rpcUrl,
});
} catch (error: any) {
assert.deepStrictEqual(error.message, 'Invalid address');
}
});

it('should throw when rpcUrl is not provided on non instantiated class', async () => {
try {
await ERC725.supportsInterface(interfaceId, {
address: contractAddress,
// @ts-ignore
rpcUrl: undefined,
});
} catch (error: any) {
assert.deepStrictEqual(error.message, 'Missing RPC URL');
}
});

// TODO: add test to test the actual behavior of the function.
});

describe('checkPermissions', () => {
const erc725Instance = new ERC725([]);

Expand Down
43 changes: 1 addition & 42 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import { decodeData } from './lib/decodeData';
import { getDataFromExternalSources } from './lib/getDataFromExternalSources';
import { DynamicKeyPart, DynamicKeyParts } from './types/dynamicKeys';
import { getData } from './lib/getData';
import { supportsInterface, checkPermissions } from './lib/detector';
import { checkPermissions } from './lib/detector';
import { decodeMappingKey } from './lib/decodeMappingKey';

export {
Expand Down Expand Up @@ -571,47 +571,6 @@ export class ERC725 {
return decodeMappingKey(keyHash, keyNameOrSchema);
}

/**
* Check if the ERC725 object supports
* a certain interface.
*
* @param interfaceIdOrName Interface ID or supported interface name.
* @returns {Promise<boolean>} if interface is supported.
*/
async supportsInterface(interfaceIdOrName: string): Promise<boolean> {
const { address, provider } = this.getAddressAndProvider();

return supportsInterface(interfaceIdOrName, {
address,
provider,
});
}

/**
* Check if a smart contract address
* supports a certain interface.
*
* @param {string} interfaceIdOrName Interface ID or supported interface name.
* @param options Object of address and RPC URL.
* @returns {Promise<boolean>} if interface is supported.
*/
static async supportsInterface(
interfaceIdOrName: string,
options: { address: string; rpcUrl: string },
): Promise<boolean> {
if (!isAddress(options.address)) {
throw new Error('Invalid address');
}
if (!options.rpcUrl) {
throw new Error('Missing RPC URL');
}

return supportsInterface(interfaceIdOrName, {
address: options.address,
provider: this.initializeProvider(options.rpcUrl),
});
}

/**
* Check if the required permissions are included in the granted permissions as defined by the LSP6 KeyManager Standard.
*
Expand Down
42 changes: 1 addition & 41 deletions src/lib/detector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,48 +21,8 @@
/* eslint-disable no-unused-expressions */

import { expect } from 'chai';
import * as sinon from 'sinon';
import { INTERFACE_IDS_0_10_2 } from '../constants/interfaces';

import { supportsInterface, checkPermissions } from './detector';

describe('supportsInterface', () => {
it('it should return true if the contract supports the interface with name', async () => {
const contractAddress = '0xcafecafecafecafecafecafecafecafecafecafe';
const interfaceName = 'LSP0ERC725Account';

const providerStub = { supportsInterface: sinon.stub() };

providerStub.supportsInterface
.withArgs(contractAddress, INTERFACE_IDS_0_10_2[interfaceName])
.returns(Promise.resolve(true));

const doesSupportInterface = await supportsInterface(interfaceName, {
address: contractAddress,
provider: providerStub,
});

expect(doesSupportInterface).to.be.true;
});

it('it should return true if the contract supports the interface with interfaceId', async () => {
const contractAddress = '0xcafecafecafecafecafecafecafecafecafecafe';
const interfaceId = INTERFACE_IDS_0_10_2.LSP1UniversalReceiver;

const providerStub = { supportsInterface: sinon.stub() };

providerStub.supportsInterface
.withArgs(contractAddress, interfaceId)
.returns(Promise.resolve(true));

const doesSupportInterface = await supportsInterface(interfaceId, {
address: contractAddress,
provider: providerStub,
});

expect(doesSupportInterface).to.be.true;
});
});
import { checkPermissions } from './detector';

describe('checkPermissions', () => {
describe('test with single permission', () => {
Expand Down
34 changes: 0 additions & 34 deletions src/lib/detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,6 @@

import { LSP6_DEFAULT_PERMISSIONS } from '../constants/constants';

import {
AddressProviderOptions,
INTERFACE_IDS_0_10_2,
} from '../constants/interfaces';

/**
* Check if a smart contract address
* supports a certain interface.
*
* @param {string} interfaceId Interface ID or supported interface name.
* @param options Object with address and RPC URL.
* @returns {Promise<boolean>} if interface is supported.
*/
export const supportsInterface = async (
interfaceIdOrName: string,
options: AddressProviderOptions,
): Promise<boolean> => {
let plainInterfaceId: string;
if (INTERFACE_IDS_0_10_2[interfaceIdOrName]) {
plainInterfaceId = INTERFACE_IDS_0_10_2[interfaceIdOrName];
} else {
plainInterfaceId = interfaceIdOrName;
}

try {
return await options.provider.supportsInterface(
options.address,
plainInterfaceId,
);
} catch (error) {
throw new Error(`Error checking the interface: ${error}`);
}
};

/**
* @notice Check if the given string is a valid 32-byte hex string.
* @param str The string to be checked.
Expand Down

0 comments on commit a164967

Please sign in to comment.