diff --git a/.eslintrc b/.eslintrc index e2d47982..3c81d22f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,7 +5,8 @@ "@colony/eslint-config-colony" ], "plugins": [ - "@typescript-eslint" + "@typescript-eslint", + "eslint-plugin-tsdoc" ], "env": { "browser": true, @@ -22,12 +23,12 @@ ] } ], - "no-unused-vars": "off", + "no-dupe-class-members": "off", "no-redeclare": "off", + "no-shadow": "off", + "no-unused-vars": "off", "no-use-before-define": "off", - "no-dupe-class-members": "off", "@typescript-eslint/no-unused-vars": "error", - "no-shadow": "off", "@typescript-eslint/no-shadow": [ "error" ], @@ -44,7 +45,8 @@ "examples/browser/src/*.ts" ] } - ] + ], + "tsdoc/syntax": "warn" }, "overrides": [ { diff --git a/docs/api/README.md b/docs/api/README.md index 5b8bf5bc..64dc9caf 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -6,6 +6,7 @@ ## Enumerations +- [ColonyLabelSuffix](enums/ColonyLabelSuffix.md) - [ColonyRole](enums/ColonyRole.md) - [Extension](enums/Extension.md) - [Id](enums/Id.md) @@ -13,6 +14,7 @@ - [MetadataType](enums/MetadataType.md) - [MotionState](enums/MotionState.md) - [Network](enums/Network.md) +- [SupportedExtension](enums/SupportedExtension.md) - [TeamColor](enums/TeamColor.md) - [Vote](enums/Vote.md) @@ -23,6 +25,7 @@ - [ColonyEventManager](classes/ColonyEventManager.md) - [ColonyNetwork](classes/ColonyNetwork.md) - [ColonyToken](classes/ColonyToken.md) +- [OneTxPayment](classes/OneTxPayment.md) - [PinataAdapter](classes/PinataAdapter.md) - [TxCreator](classes/TxCreator.md) - [VotingReputation](classes/VotingReputation.md) @@ -33,9 +36,11 @@ - [ColonyEvent](interfaces/ColonyEvent.md) - [ColonyEventManagerOptions](interfaces/ColonyEventManagerOptions.md) - [ColonyFilter](interfaces/ColonyFilter.md) +- [ColonyMetadata](interfaces/ColonyMetadata.md) - [ColonyMultiFilter](interfaces/ColonyMultiFilter.md) - [ColonyNetworkOptions](interfaces/ColonyNetworkOptions.md) - [ColonyTopic](interfaces/ColonyTopic.md) +- [DomainMetadata](interfaces/DomainMetadata.md) - [Ethers6Filter](interfaces/Ethers6Filter.md) - [Ethers6FilterByBlockHash](interfaces/Ethers6FilterByBlockHash.md) - [EventSources](interfaces/EventSources.md) diff --git a/docs/api/classes/Colony.md b/docs/api/classes/Colony.md index 60246efc..a6c40ed8 100644 --- a/docs/api/classes/Colony.md +++ b/docs/api/classes/Colony.md @@ -42,9 +42,9 @@ ___ ___ -### SupportedVersions +### supportedVersions -▪ `Static` **SupportedVersions**: ``10``[] +▪ `Static` **supportedVersions**: ``10``[] The currently supported Colony version. If a Colony is not on this version it has to be upgraded. If this is not an option, Colony SDK might throw errors at certain points. Usage of ColonyJS is advised in these cases @@ -70,12 +70,12 @@ If [AnnotationMetadata](../interfaces/AnnotationMetadata.md) is provided directl (async function() { // Create a motion to pay 10 of the native token to some (maybe your own?) address - // (forced transaction example) const [, { transactionHash }] = await colony.ext.oneTx.pay( '0xb77D57F4959eAfA0339424b83FcFaf9c15407461', w`10`, ).motion(); // Annotate the motion transaction with a little explanation :) + // (forced transaction example) await colony.annotateTransaction( transactionHash, { annotationMsg: 'I am creating this motion because I think I deserve a little bonus' }, @@ -173,7 +173,7 @@ import { TeamColor } from '@colony/sdk'; | Name | Type | Description | | :------ | :------ | :------ | -| `metadata` | `string` \| `DomainMetadata` | The team metadata you would like to add (or an IPFS CID pointing to valid metadata). If DomainMetadata is provided directly (as opposed to a [CID](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) for a JSON file) this requires an [IpfsAdapter](../interfaces/IpfsAdapter.md) that can upload and pin to IPFS (like the [PinataAdapter](PinataAdapter.md)). See its documentation for more information. | +| `metadata` | `string` \| [`DomainMetadata`](../interfaces/DomainMetadata.md) | The team metadata you would like to add (or an IPFS CID pointing to valid metadata). If [DomainMetadata](../interfaces/DomainMetadata.md) is provided directly (as opposed to a [CID](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) for a JSON file) this requires an [IpfsAdapter](../interfaces/IpfsAdapter.md) that can upload and pin to IPFS (like the [PinataAdapter](PinataAdapter.md)). See its documentation for more information. | #### Returns @@ -361,6 +361,57 @@ A Team object ___ +### installExtension + +▸ **installExtension**(`extension`): [`TxCreator`](TxCreator.md)<`ColonyClientV10`, ``"installExtension"``, { `colony?`: `string` ; `extensionId?`: `string` ; `version?`: `BigNumber` }, [`MetadataType`](../enums/MetadataType.md)\> + +Install an extension for a colony + +Valid extensions can be found here: [SupportedExtension](../enums/SupportedExtension.md) + +**`Remarks`** + +Be aware that some extensions need some extra setup steps (like the `initialise` method on `VotingReputation`). +After an extension was installed, `colony.updateExtensions()` needs to be called (see example) + +**`Example`** + +```typescript +// Immediately executing async function +(async function() { + // Install the OneTxPayment extension for Colony + // (forced transaction example) + await colony.installExtension( + SupportedExtension.oneTx, + ).force(); + // Update the extensions in the colony + await colony.updateExtensions(); + console.info(colony.ext.oneTx.address); +})(); +``` + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `extension` | [`SupportedExtension`](../enums/SupportedExtension.md) | Name of the extension you'd like to install | + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyClientV10`, ``"installExtension"``, { `colony?`: `string` ; `extensionId?`: `string` ; `version?`: `BigNumber` }, [`MetadataType`](../enums/MetadataType.md)\> + +A [TxCreator](TxCreator.md) + +**Event data** + +| Property | Type | Description | +| :------ | :------ | :------ | +| `extensionId` | string | Id (name) of the extension (e.g. `OneTxPayment`) | +| `colony` | string | The address of the colony on which the extension was installed | +| `version` | BigNumber | The version of the extension that was installed | + +___ + ### makeArbitraryTransaction ▸ **makeArbitraryTransaction**(`target`, `action`): [`TxCreator`](TxCreator.md)<`ColonyClientV10`, ``"makeArbitraryTransactions"``, `Record`<`string`, `unknown`\>, [`MetadataType`](../enums/MetadataType.md)\> @@ -463,3 +514,131 @@ A [TxCreator](TxCreator.md) | `toPot` | BigNumber | The target funding pot | | `amount` | BigNumber | The amount that was transferred | | `token` | string | The token address being transferred | + +___ + +### setRoles + +▸ **setRoles**(`address`, `roles`, `teamId?`): [`TxCreator`](TxCreator.md)<`ColonyClientV10`, ``"setUserRoles"``, { `agent?`: `string` ; `domainId?`: `BigNumber` ; `role?`: `number` ; `setTo?`: `boolean` ; `user?`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +Set (award) roles to a user/contract + +**`Remarks`** + +Existing roles will be kept. Use [unsetRoles](Colony.md#unsetroles) to remove roles + +**`Example`** + +```typescript +import { ColonyRole } from '@colony/sdk'; + +// Immediately executing async function +(async function() { + // Give Administration and Root role to address 0xb794f5ea0ba39494ce839613fffba74279579268 (in Root team) + // (forced transaction example) + await colony.setRoles( + '0xb794f5ea0ba39494ce839613fffba74279579268', + [ColonyRole.Administration, ColonyRole.Root], + ).force(); +})(); +``` + +#### Parameters + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `address` | `string` | `undefined` | Address of the wallet or contract to give the roles to | +| `roles` | [`ColonyRole`](../enums/ColonyRole.md) \| [`ColonyRole`](../enums/ColonyRole.md)[] | `undefined` | Role or array of roles to award | +| `teamId` | `BigNumberish` | `Id.RootDomain` | Team to apply the role(s) in | + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyClientV10`, ``"setUserRoles"``, { `agent?`: `string` ; `domainId?`: `BigNumber` ; `role?`: `number` ; `setTo?`: `boolean` ; `user?`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +A [TxCreator](TxCreator.md) + +**Event data** +Heads up!* This event is emitted for every role that was set + +| Property | Type | Description | +| :------ | :------ | :------ | +| `agent` | string | The address that is responsible for triggering this event | +| `user` | string | Address of the user who was awarded the role | +| `domainId` | BigNumber | The team the role was awarded for | +| `role` | number | The number of the role that was awarded. Use `ColonyRole[role]` to get the title of the role | +| `setTo` | number | Whether the role was awarded or removed | + +___ + +### unsetRoles + +▸ **unsetRoles**(`address`, `roles`, `teamId?`): [`TxCreator`](TxCreator.md)<`ColonyClientV10`, ``"setUserRoles"``, { `agent?`: `string` ; `domainId?`: `BigNumber` ; `role?`: `number` ; `setTo?`: `boolean` ; `user?`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +Unset (remove) roles from a user/contract + +#### Parameters + +| Name | Type | Default value | Description | +| :------ | :------ | :------ | :------ | +| `address` | `string` | `undefined` | Address of the wallet or contract to remove the roles from | +| `roles` | [`ColonyRole`](../enums/ColonyRole.md) \| [`ColonyRole`](../enums/ColonyRole.md)[] | `undefined` | Role or array of roles to remove | +| `teamId` | `BigNumberish` | `Id.RootDomain` | Team to apply the role(s) in | + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyClientV10`, ``"setUserRoles"``, { `agent?`: `string` ; `domainId?`: `BigNumber` ; `role?`: `number` ; `setTo?`: `boolean` ; `user?`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +A [TxCreator](TxCreator.md) + +**Event data** +Heads up!* This event is emitted for every role that was unset + +| Property | Type | Description | +| :------ | :------ | :------ | +| `agent` | string | The address that is responsible for triggering this event | +| `user` | string | Address of the user of which the role was removed | +| `domainId` | BigNumber | The team the role was removed for | +| `role` | number | The number of the role that was removed. Use `ColonyRole[role]` to get the title of the role | +| `setTo` | number | Whether the role was awarded or removed | + +___ + +### updateExtensions + +▸ **updateExtensions**(): `Promise`<`void`\> + +Refresh colony extensions + +Call this function after a new extension was installed. +It will then become available under `colony.ext` + +#### Returns + +`Promise`<`void`\> + +___ + +### getLatestSupportedVersion + +▸ `Static` **getLatestSupportedVersion**(): ``10`` + +#### Returns + +``10`` + +___ + +### init + +▸ `Static` **init**(`colonyNetwork`, `colonyClient`): `Promise`<[`Colony`](Colony.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `colonyNetwork` | [`ColonyNetwork`](ColonyNetwork.md) | +| `colonyClient` | `ColonyClientV10` | + +#### Returns + +`Promise`<[`Colony`](Colony.md)\> diff --git a/docs/api/classes/ColonyEventManager.md b/docs/api/classes/ColonyEventManager.md index 1783f8e7..c98745a8 100644 --- a/docs/api/classes/ColonyEventManager.md +++ b/docs/api/classes/ColonyEventManager.md @@ -89,7 +89,7 @@ Filter for all `DomainAdded` events between block 21830000 and 21840000 (across | `eventName` | `N` | A valid event signature from the contract's `filters` object | | `address?` | `string` | Address of the contract that can emit this event | | `params?` | `Parameters`<`T`[``"filters"``][`N`]\> | Parameters to filter by for the event. Has to be indexed in the contract (see _ethers_ [Event Filters](https://docs.ethers.io/v5/api/contract/contract/#Contract--filters)) | -| `options?` | `Object` | You can define `fromBlock` and `toBlock` only once for all the filters given | +| `options?` | `Object` | You can define `fromBlock` and `toBlock` only once for all the filters given (default for both is `latest`) | | `options.fromBlock?` | `BlockTag` | - | | `options.toBlock?` | `BlockTag` | - | @@ -238,9 +238,9 @@ const domainMetadata = colonyEvents.createMultiFilter( | Name | Type | Description | | :------ | :------ | :------ | | `filters` | [`ColonyMultiFilter`](../interfaces/ColonyMultiFilter.md)[] | An array of [ColonyMultiFilter](../interfaces/ColonyMultiFilter.md)s. Normal [ColonyFilter](../interfaces/ColonyFilter.md)s will not work | -| `options` | `Object` | You can define `fromBlock` and `toBlock` only once for all the filters given | -| `options.fromBlock?` | `BlockTag` | Starting block in which to look for this event - inclusive (default: 'latest') | -| `options.toBlock?` | `BlockTag` | Ending block in which to look for this event - inclusive (default: 'latest') | +| `options` | `Object` | You can define `fromBlock` and `toBlock` only once for all the filters given (default for both is `latest`) | +| `options.fromBlock?` | `BlockTag` | - | +| `options.toBlock?` | `BlockTag` | - | #### Returns diff --git a/docs/api/classes/ColonyNetwork.md b/docs/api/classes/ColonyNetwork.md index b271619c..df5f3500 100644 --- a/docs/api/classes/ColonyNetwork.md +++ b/docs/api/classes/ColonyNetwork.md @@ -54,8 +54,156 @@ ___ • **networkClient**: `ColonyNetworkClient` +___ + +### signerOrProvider + +• **signerOrProvider**: `SignerOrProvider` + ## Methods +### createColony + +▸ **createColony**(`tokenAddress`, `label`, `metadata`): [`TxCreator`](TxCreator.md)<`ColonyNetworkClient`, ``"createColony(address,uint256,string,string)"``, { `agent`: `string` ; `colonyAddress`: `string` ; `colonyId`: `BigNumber` ; `metadata`: `string` ; `token`: `string` }, [`Colony`](../enums/MetadataType.md#colony)\> + +Create a new Colony with metadata + +Creates a new Colony with IPFS metadata. To edit metadata at a later point you can call the Colony.editColony method. + +**`Remarks`** + +There is more to creating a fully functional colony that can be used within the dapp than just calling this function. See the [Colony Creation Guide](../../guides/colony-creation.md). + +**`Example`** + +```typescript +import { Tokens } from '@colony/sdk'; + +// Immediately executing async function +(async function() { + // Create a colony with some metadata details attached + // (forced transaction example) + // (also notice that this requires an upload-capable IPFS adapter) + await colonyNetwork.createColony( + // Use USDC on Gnosis chain as the native token + '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83', { + colonyDisplayName: 'Cool Colony', + // IPFS hash to an image file + colonyAvatarHash: 'QmS26o1Cmsrx7iw1SSFGEcy22TVDq6VmEZ4XNjpWFyaKUe', + // List of token addresses that the Colony should be initialized with (can be changed later) - excluding ETH and the native token from above + colonyTokens: [Tokens.CLNY], + }).force(); +})(); +``` + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `tokenAddress` | `string` | - | +| `label` | `string` | - | +| `metadata` | `string` \| [`ColonyMetadata`](../interfaces/ColonyMetadata.md) | The team metadata you would like to add (or an IPFS CID pointing to valid metadata). If [ColonyMetadata](../interfaces/ColonyMetadata.md) is provided directly (as opposed to a [CID](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) for a JSON file) this requires an [IpfsAdapter](../interfaces/IpfsAdapter.md) that can upload and pin to IPFS (like the [PinataAdapter](PinataAdapter.md)). See its documentation for more information. | + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyNetworkClient`, ``"createColony(address,uint256,string,string)"``, { `agent`: `string` ; `colonyAddress`: `string` ; `colonyId`: `BigNumber` ; `metadata`: `string` ; `token`: `string` }, [`Colony`](../enums/MetadataType.md#colony)\> + +A [TxCreator](TxCreator.md) + +**Event data** + +| Property | Type | Description | +| :------ | :------ | :------ | +| `colonyId` | BigNumber | Auto-incremented integer id of the colony | +| `colonyAddress` | string | Address of the newly deployed colony contract | +| `token` | string | Address of the token that is used as the colony's native token | +| `metadata` | string | IPFS CID of metadata attached to this transaction | + +**Metadata** (can be obtained by calling and awaiting the `getMetadata` function) + +| Property | Type | Description | +| :------ | :------ | :------ | +| `colonyDisplayName` | string | The name that should be displayed for the colony | +| `colonyAvatarHash` | string | An IPFS hash for a Colony logo (make it 200x200px) | +| `colonyTokens` | string[] | A list of additional tokens that should be in the colony's "address book" | + +▸ **createColony**(`tokenAddress`, `label`): [`TxCreator`](TxCreator.md)<`ColonyNetworkClient`, ``"createColony(address,uint256,string)"``, { `colonyAddress`: `string` ; `colonyId`: `BigNumber` ; `metadata?`: `undefined` ; `token`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +Create a new Colony without metadata + +Creates a new Colony without IPFS metadata. To add metadata at a later point you can call the Colony.editColony method. + +**`Remarks`** + +There is more to creating a fully functional colony that can be used within the dapp than just calling this function. See the [Colony Creation Guide](../../guides/colony-creation.md). + +**`Example`** + +```typescript +// Immediately executing async function +(async function() { + // Create a colony + // (forced transaction example) + await colonyNetwork + // Use USDC on Gnosis chain as the native token + .createColony('0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83') + .force(); +})(); +``` + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `tokenAddress` | `string` | +| `label` | `string` | + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyNetworkClient`, ``"createColony(address,uint256,string)"``, { `colonyAddress`: `string` ; `colonyId`: `BigNumber` ; `metadata?`: `undefined` ; `token`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +A [TxCreator](TxCreator.md) + +**Event data** + +| Property | Type | Description | +| :------ | :------ | :------ | +| `colonyId` | BigNumber | Auto-incremented integer id of the colony | +| `colonyAddress` | string | Address of the newly deployed colony contract | +| `token` | string | Address of the token that is used as the colony's native token | + +___ + +### deployToken + +▸ **deployToken**(`name`, `symbol`, `decimals?`): [`TxCreator`](TxCreator.md)<`ColonyNetworkClient`, ``"deployTokenViaNetwork"``, { `tokenAddress?`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +Deploy a "special" colony ERC20 token + +If there is not token yet that should be used with the Colony, this is the canonical way to create one. + +This is a supercharged ERC20 token contract, that not only has a permissioned `mint` function (that can be used from the colony) but also supports Metatransactions. In order to fully use its permissioned system with a colony, some extra steps have to be taken. See the [Colony Creation Guide](../../guides/colony-creation.md). + +**`Remarks`** + +The token deployed with this function is locked by default. Call `unlockToken()` on the Colony at a later point to unlock it. + +#### Parameters + +| Name | Type | Default value | +| :------ | :------ | :------ | +| `name` | `string` | `undefined` | +| `symbol` | `string` | `undefined` | +| `decimals` | `number` | `18` | + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyNetworkClient`, ``"deployTokenViaNetwork"``, { `tokenAddress?`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +The colony's address + +___ + ### getColony ▸ **getColony**(`address`): `Promise`<[`Colony`](Colony.md)\> @@ -82,6 +230,52 @@ A Colony abstaction instance ___ +### getColonyAddress + +▸ **getColonyAddress**(`label`): `Promise`<``null`` \| `string`\> + +Get the colony's addess by the ENS label + +Returns the colony's address that belongs to the given ENS label +Will return `null` if the given label was not assigned to a colony. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `label` | `string` | + +#### Returns + +`Promise`<``null`` \| `string`\> + +The colony's address + +___ + +### getColonyLabel + +▸ **getColonyLabel**(`address`): `Promise`<``null`` \| `string`\> + +Get the colony's ENS label + +Returns the colony's ENS label, just like it's shown in the browsers address bar after `/colony/`, when using the dApp. +Will return `null` if the colony does not exist or if no label was assigned yet + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `address` | `string` | + +#### Returns + +`Promise`<``null`` \| `string`\> + +The colony's ENS label + +___ + ### getMetaColony ▸ **getMetaColony**(): `Promise`<[`Colony`](Colony.md)\> diff --git a/docs/api/classes/ColonyToken.md b/docs/api/classes/ColonyToken.md index 64ff0d2e..500a7cc1 100644 --- a/docs/api/classes/ColonyToken.md +++ b/docs/api/classes/ColonyToken.md @@ -2,6 +2,12 @@ ## Properties +### address + +• **address**: `string` + +___ + ### tokenLockingClient • **tokenLockingClient**: `TokenLockingClient` @@ -53,6 +59,22 @@ A tupel of event data and contract receipt ___ +### deployAuthority + +▸ **deployAuthority**(`allowedToTransfer?`): [`TxCreator`](TxCreator.md)<`ColonyNetworkClient`, ``"deployTokenAuthority"``, { `tokenAuthorityAddress?`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `allowedToTransfer?` | `string`[] | + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyNetworkClient`, ``"deployTokenAuthority"``, { `tokenAuthorityAddress?`: `string` }, [`MetadataType`](../enums/MetadataType.md)\> + +___ + ### deposit ▸ **deposit**(`amount`): `Promise`<[{ `amount?`: `BigNumber` ; `token?`: `string` ; `user?`: `string` }, `ContractReceipt`]\> @@ -184,6 +206,32 @@ A [TxCreator](TxCreator.md) ___ +### setAuthority + +▸ **setAuthority**(`tokenAuthorityAddress`): [`TxCreator`](TxCreator.md)<`ColonyTokenClient`, ``"setAuthority"``, `Record`<`string`, `unknown`\>, [`MetadataType`](../enums/MetadataType.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `tokenAuthorityAddress` | `string` | + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyTokenClient`, ``"setAuthority"``, `Record`<`string`, `unknown`\>, [`MetadataType`](../enums/MetadataType.md)\> + +___ + +### setupColonyAsOwner + +▸ **setupColonyAsOwner**(): [`TxCreator`](TxCreator.md)<`ColonyTokenClient`, ``"setOwner"``, `Record`<`string`, `unknown`\>, [`MetadataType`](../enums/MetadataType.md)\> + +#### Returns + +[`TxCreator`](TxCreator.md)<`ColonyTokenClient`, ``"setOwner"``, `Record`<`string`, `unknown`\>, [`MetadataType`](../enums/MetadataType.md)\> + +___ + ### symbol ▸ **symbol**(): `Promise`<`string`\> diff --git a/docs/api/classes/OneTxPayment.md b/docs/api/classes/OneTxPayment.md new file mode 100644 index 00000000..cf6f9a0f --- /dev/null +++ b/docs/api/classes/OneTxPayment.md @@ -0,0 +1,107 @@ +# Class: OneTxPayment + +## `OneTxPayment` (One Transaction Payment) + +Ordinarily payments require more than one transaction, because the payment lifecycle requires more than one permissioned role. + +In some use cases, there might be a need for one authorized individual to be able to create, funds, and finalize a payment within a single transaction. + +The OneTxPayment extension adds this functionality by adding a makePayment function which requires the caller to have both Funding and administration ability within the domain of the payment. + +Extension therefore requires Administration and Funding roles to function. + +Note: if you deployed your Colony using the Dapp, the OneTxPayment extension is already installed for you + +## Constructors + +### constructor + +• **new OneTxPayment**(`colony`, `oneTxPaymentClient`) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `colony` | [`Colony`](Colony.md) | +| `oneTxPaymentClient` | `OneTxPaymentClientV3` | + +## Properties + +### address + +• **address**: `string` + +___ + +### extensionType + +▪ `Static` **extensionType**: [`OneTxPayment`](../enums/Extension.md#onetxpayment) = `Extension.OneTxPayment` + +___ + +### supportedVersion + +▪ `Static` **supportedVersion**: ``3``[] + +## Methods + +### pay + +▸ **pay**(`recipient`, `amount`, `teamId?`, `tokenAddress?`): [`TxCreator`](TxCreator.md)<`OneTxPaymentClientV3`, ``"makePaymentFundedFromDomain"``, { `agent?`: `string` ; `fundamentalId?`: `BigNumber` ; `nPayouts?`: `BigNumber` }, [`MetadataType`](../enums/MetadataType.md)\> + +Make a payment to a single address using a single token + +**`Remarks`** + +Requires the `OneTxPayment` extension to be installed for the Colony (this is usually the case for Colonies created via the Dapp). Note that most tokens use 18 decimals, so add a bunch of zeros or use our `w` or `toWei` functions (see example) + +**`Example`** + +```typescript +import { Id, Tokens, w } from '@colony/sdk'; + +// Immediately executing async function +(async function() { + // Pay 10 XDAI (on Gnosis chain) from the root domain to the following address + // (forced transaction example) + await colony.pay( + '0xb77D57F4959eAfA0339424b83FcFaf9c15407461', + w`10`, + Id.RootDomain, + Tokens.Gnosis.XDAI, + ).force(); +})(); +``` + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `recipient` | `string` | Wallet address of account to send the funds to (also awarded reputation when sending the native token) | +| `amount` | `BigNumberish` | Amount to pay in wei | +| `teamId?` | `BigNumberish` | The team to use to send the funds from. Has to have funding of at least the amount you need to send. See [Colony.moveFundsToTeam](Colony.md#movefundstoteam). Defaults to the Colony's root team | +| `tokenAddress?` | `string` | The address of the token to make the payment in. Default is the Colony's native token | + +#### Returns + +[`TxCreator`](TxCreator.md)<`OneTxPaymentClientV3`, ``"makePaymentFundedFromDomain"``, { `agent?`: `string` ; `fundamentalId?`: `BigNumber` ; `nPayouts?`: `BigNumber` }, [`MetadataType`](../enums/MetadataType.md)\> + +A [TxCreator](TxCreator.md) + +**Event data** + +| Property | Type | Description | +| :------ | :------ | :------ | +| `agent` | string | The address that is responsible for triggering this event | +| `fundamentalId` | BigNumber | The newly added payment id | +| `nPayouts` | BigNumber | Number of payouts in total | + +___ + +### getLatestSupportedVersion + +▸ `Static` **getLatestSupportedVersion**(): ``3`` + +#### Returns + +``3`` diff --git a/docs/api/classes/TxCreator.md b/docs/api/classes/TxCreator.md index 68375d40..4b85b019 100644 --- a/docs/api/classes/TxCreator.md +++ b/docs/api/classes/TxCreator.md @@ -46,7 +46,8 @@ Learn more about these functions in their individual documentation | :------ | :------ | | `__namedParameters` | `Object` | | `__namedParameters.args` | `unknown`[] \| () => `Promise`<`unknown`[]\> | -| `__namedParameters.colony` | [`Colony`](Colony.md) | +| `__namedParameters.colony?` | [`Colony`](Colony.md) | +| `__namedParameters.colonyNetwork` | [`ColonyNetwork`](ColonyNetwork.md) | | `__namedParameters.contract` | `C` | | `__namedParameters.eventData?` | (`receipt`: `ContractReceipt`) => `Promise`<`E`\> | | `__namedParameters.metadataType?` | `MD` | @@ -57,7 +58,7 @@ Learn more about these functions in their individual documentation ### force -▸ **force**(): `Promise`<[`E`, `ContractReceipt`, () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `undefined` \| `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => `undefined` \| `ColonyMetadata` = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `undefined` \| `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => `undefined` \| `DomainMetadata` = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `undefined` \| `MiscMetadata` = getMiscDataFromResponse }[`MD`]\>\>] \| [`E`, `ContractReceipt`]\> +▸ **force**(): `Promise`<[`E`, `ContractReceipt`, () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => [`ColonyMetadata`](../interfaces/ColonyMetadata.md) = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => [`DomainMetadata`](../interfaces/DomainMetadata.md) = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `MiscMetadata` = getMiscDataFromResponse }[`MD`]\>\>] \| [`E`, `ContractReceipt`]\> Forces an action @@ -67,7 +68,7 @@ The user sending this transaction has to have the appropriate permissions to do #### Returns -`Promise`<[`E`, `ContractReceipt`, () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `undefined` \| `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => `undefined` \| `ColonyMetadata` = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `undefined` \| `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => `undefined` \| `DomainMetadata` = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `undefined` \| `MiscMetadata` = getMiscDataFromResponse }[`MD`]\>\>] \| [`E`, `ContractReceipt`]\> +`Promise`<[`E`, `ContractReceipt`, () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => [`ColonyMetadata`](../interfaces/ColonyMetadata.md) = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => [`DomainMetadata`](../interfaces/DomainMetadata.md) = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `MiscMetadata` = getMiscDataFromResponse }[`MD`]\>\>] \| [`E`, `ContractReceipt`]\> A tupel of event data and contract receipt (and a function to retrieve metadata if applicable) @@ -75,7 +76,7 @@ ___ ### forceMeta -▸ **forceMeta**(): `Promise`<[`E`, [`ParsedLogTransactionReceipt`](../interfaces/ParsedLogTransactionReceipt.md), () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `undefined` \| `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => `undefined` \| `ColonyMetadata` = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `undefined` \| `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => `undefined` \| `DomainMetadata` = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `undefined` \| `MiscMetadata` = getMiscDataFromResponse }[`MD`]\>\>] \| [`E`, [`ParsedLogTransactionReceipt`](../interfaces/ParsedLogTransactionReceipt.md)]\> +▸ **forceMeta**(): `Promise`<[`E`, [`ParsedLogTransactionReceipt`](../interfaces/ParsedLogTransactionReceipt.md), () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => [`ColonyMetadata`](../interfaces/ColonyMetadata.md) = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => [`DomainMetadata`](../interfaces/DomainMetadata.md) = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `MiscMetadata` = getMiscDataFromResponse }[`MD`]\>\>] \| [`E`, [`ParsedLogTransactionReceipt`](../interfaces/ParsedLogTransactionReceipt.md)]\> Forces an action using a gasless metatransaction @@ -85,7 +86,7 @@ The user sending this transaction has to have the appropriate permissions to do #### Returns -`Promise`<[`E`, [`ParsedLogTransactionReceipt`](../interfaces/ParsedLogTransactionReceipt.md), () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `undefined` \| `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => `undefined` \| `ColonyMetadata` = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `undefined` \| `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => `undefined` \| `DomainMetadata` = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `undefined` \| `MiscMetadata` = getMiscDataFromResponse }[`MD`]\>\>] \| [`E`, [`ParsedLogTransactionReceipt`](../interfaces/ParsedLogTransactionReceipt.md)]\> +`Promise`<[`E`, [`ParsedLogTransactionReceipt`](../interfaces/ParsedLogTransactionReceipt.md), () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => [`ColonyMetadata`](../interfaces/ColonyMetadata.md) = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => [`DomainMetadata`](../interfaces/DomainMetadata.md) = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `MiscMetadata` = getMiscDataFromResponse }[`MD`]\>\>] \| [`E`, [`ParsedLogTransactionReceipt`](../interfaces/ParsedLogTransactionReceipt.md)]\> A tupel of event data and contract receipt (and a function to retrieve metadata if applicable) diff --git a/docs/api/classes/VotingReputation.md b/docs/api/classes/VotingReputation.md index 23a85c0f..58a1876c 100644 --- a/docs/api/classes/VotingReputation.md +++ b/docs/api/classes/VotingReputation.md @@ -102,6 +102,18 @@ You can - at any point in the lifecycle inspect the current state of a Motion. U • **address**: `string` +___ + +### extensionType + +▪ `Static` **extensionType**: [`IVotingReputation`](../enums/Extension.md#ivotingreputation) = `Extension.IVotingReputation` + +___ + +### supportedVersions + +▪ `Static` **supportedVersions**: ``7``[] + ## Methods ### approveStake @@ -427,3 +439,13 @@ A tupel of event data and contract receipt | :------ | :------ | :------ | | `motionId` | BigNumber | ID of the motion created | | `voter` | string | The address of the user who voted | + +___ + +### getLatestSupportedVersion + +▸ `Static` **getLatestSupportedVersion**(): ``7`` + +#### Returns + +``7`` diff --git a/docs/api/enums/ColonyLabelSuffix.md b/docs/api/enums/ColonyLabelSuffix.md new file mode 100644 index 00000000..ef040b75 --- /dev/null +++ b/docs/api/enums/ColonyLabelSuffix.md @@ -0,0 +1,37 @@ +# Enumeration: ColonyLabelSuffix + +## Enumeration Members + +### Custom + +• **Custom** = ``".colony.joincolony.test"`` + +___ + +### Gnosis + +• **Gnosis** = ``".colony.joincolony.colonyxdai"`` + +___ + +### Goerli + +• **Goerli** = ``".colony.joincolony.test"`` + +___ + +### Mainnet + +• **Mainnet** = ``".colony.joincolony.eth"`` + +___ + +### Xdai + +• **Xdai** = ``".colony.joincolony.colonyxdai"`` + +___ + +### XdaiQa + +• **XdaiQa** = ``".colony.joincolony.colonyxdai"`` diff --git a/docs/api/enums/SupportedExtension.md b/docs/api/enums/SupportedExtension.md new file mode 100644 index 00000000..865a549a --- /dev/null +++ b/docs/api/enums/SupportedExtension.md @@ -0,0 +1,19 @@ +# Enumeration: SupportedExtension + +Extensions that are supported by Colony SDK + +## Enumeration Members + +### motion + +• **motion** = ``"motion"`` + +Motions & Disputes (VotingReputation) + +___ + +### oneTx + +• **oneTx** = ``"oneTx"`` + +One Transaction Payment (OneTxPayment - enabled by default in Dapp created colonies) diff --git a/docs/api/interfaces/ColonyEvent.md b/docs/api/interfaces/ColonyEvent.md index 69a1e0a6..106f468d 100644 --- a/docs/api/interfaces/ColonyEvent.md +++ b/docs/api/interfaces/ColonyEvent.md @@ -68,15 +68,15 @@ ___ ### getMetadata -• `Optional` **getMetadata**: () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `undefined` \| `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => `undefined` \| `ColonyMetadata` = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `undefined` \| `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => `undefined` \| `DomainMetadata` = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `undefined` \| `MiscMetadata` = getMiscDataFromResponse }[`T`]\>\> +• `Optional` **getMetadata**: () => `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => [`ColonyMetadata`](ColonyMetadata.md) = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => [`DomainMetadata`](DomainMetadata.md) = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `MiscMetadata` = getMiscDataFromResponse }[`T`]\>\> #### Type declaration -▸ (): `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `undefined` \| `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => `undefined` \| `ColonyMetadata` = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `undefined` \| `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => `undefined` \| `DomainMetadata` = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `undefined` \| `MiscMetadata` = getMiscDataFromResponse }[`T`]\>\> +▸ (): `Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => [`ColonyMetadata`](ColonyMetadata.md) = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => [`DomainMetadata`](DomainMetadata.md) = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `MiscMetadata` = getMiscDataFromResponse }[`T`]\>\> ##### Returns -`Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `undefined` \| `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => `undefined` \| `ColonyMetadata` = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `undefined` \| `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => `undefined` \| `DomainMetadata` = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `undefined` \| `MiscMetadata` = getMiscDataFromResponse }[`T`]\>\> +`Promise`<`ReturnType`<{ `None`: () => `void` ; `annotation`: (`res`: `string`) => `string` = getAnnotationMsgFromResponse; `colony`: (`res`: `string`) => [`ColonyMetadata`](ColonyMetadata.md) = getColonyMetadataFromResponse; `decision`: (`res`: `string`) => `DecisionMetadata` = getDecisionDetailsFromResponse; `domain`: (`res`: `string`) => [`DomainMetadata`](DomainMetadata.md) = getDomainMetadataFromResponse; `misc`: (`res`: `string`) => `MiscMetadata` = getMiscDataFromResponse }[`T`]\>\> ___ diff --git a/docs/api/interfaces/ColonyMetadata.md b/docs/api/interfaces/ColonyMetadata.md new file mode 100644 index 00000000..97a4cc47 --- /dev/null +++ b/docs/api/interfaces/ColonyMetadata.md @@ -0,0 +1,43 @@ +# Interface: ColonyMetadata + +## Properties + +### colonyAvatarHash + +• `Optional` **colonyAvatarHash**: ``null`` \| `string` + +___ + +### colonyDisplayName + +• `Optional` **colonyDisplayName**: `string` + +___ + +### colonyName + +• `Optional` **colonyName**: `string` + +___ + +### colonySafes + +• `Optional` **colonySafes**: `SafeMetadata`[] + +___ + +### colonyTokens + +• `Optional` **colonyTokens**: `string`[] + +___ + +### isWhitelistActivated + +• `Optional` **isWhitelistActivated**: `boolean` + +___ + +### verifiedAddresses + +• `Optional` **verifiedAddresses**: `string`[] diff --git a/docs/api/interfaces/DomainMetadata.md b/docs/api/interfaces/DomainMetadata.md new file mode 100644 index 00000000..5f451908 --- /dev/null +++ b/docs/api/interfaces/DomainMetadata.md @@ -0,0 +1,19 @@ +# Interface: DomainMetadata + +## Properties + +### domainColor + +• `Optional` **domainColor**: `number` + +___ + +### domainName + +• `Optional` **domainName**: `string` + +___ + +### domainPurpose + +• `Optional` **domainPurpose**: `string` diff --git a/docs/api/interfaces/SupportedExtensions.md b/docs/api/interfaces/SupportedExtensions.md index 16128fb9..d853d0f8 100644 --- a/docs/api/interfaces/SupportedExtensions.md +++ b/docs/api/interfaces/SupportedExtensions.md @@ -10,4 +10,4 @@ ___ ### oneTx -• `Optional` **oneTx**: `OneTxPayment` +• `Optional` **oneTx**: [`OneTxPayment`](../classes/OneTxPayment.md) diff --git a/docs/guides/colony-creation.md b/docs/guides/colony-creation.md new file mode 100644 index 00000000..0179ec92 --- /dev/null +++ b/docs/guides/colony-creation.md @@ -0,0 +1,97 @@ +--- +description: A guide on how to create a colony programmatically. The deployment of a colony requires a handful of transactions for it to be up and running and fully usable. This guide explains how to go through the whole process using Colony SDK + +sidebar_position: 1 +--- + +# Creating a colony + +Even though deploying a Colony is technically just a matter of issuing one transaction, for the colony to be properly set up and usable in the dApp, some extra steps are necessary. In this guide we'll walk you through the whole process of creating the right transactions and explain what happens on the way. + +**Keep in mind that some of these transactions are optional and depend on your specific situation.** + + +For a full example see [here](https://github.com/JoinColony/colonySDK/blob/main/examples/node/create.ts). + +:::info +These examples assume that the user executing the transactions has funds in their wallet to pay for gas. If you'd like to use gasless transactions instead, use `forceMeta()` instead of `force()`. +::: + +## Step 1 (optional) - Creating a token + +If you don't have a token contract deployed yet that you wish to use as the native token in your colony, deploying it using the Colony Network is highly recommended. That will give you a better integration and control over your token from your colony (e.g. using the `mintTokens` function on the colony). + +To deploy a token, call the `deployToken` method on `ColonyNetwork`: + +```typescript +// Create token to be used with Colony +const [{ tokenAddress }] = await colonyNetwork + .deployToken('Test token', 'TOT') + .force(); +console.info('Token address', tokenAddress); +``` + +One can specify the token name, its symbol and even its decimals (even though it's recommended to leave that at the default value). This will deploy a special ERC20 token that integrates well with Colony (e.g. it supports permissions, minting and gasless transactions out of the box). For its contract code see [here](https://github.com/JoinColony/colonyNetwork/blob/develop/contracts/metaTxToken/MetaTxToken.sol). + +## Step 2 - Deploying the colony contract + +The most important step. Here the actualy colony contract will be deployed. This happens by executing a contract method on the `ColonyNetwork` (as opposed to a deploy-transaction): + +```typescript +// Create actual colony (deploys Colony contract) +const [{ colonyAddress }] = await colonyNetwork + .createColony(tokenAddress, 'colonytestname') + .force(); +``` + +Here a label for the colony can be assigned. These are unique, so pick one that's not already taken. The `createColony` method will check that. Alternatively, the `colonyNetwork.getColonyAddress(label)` function can be used. + +**If the own token was used and no extension installation is desired we would be done here. This is not recommended though, as one will at least need the `OneTxPayment` extension to properly use Colony at this stage. + +## Step 3 - Instantiate the Colony for the first time + +Let's instantiate the colony (this is the code used to instantiate an existing colony) and the token: + +```typescript +const colony = await colonyNetwork.getColony(colonyAddress); +const token = await colony.getToken(); +``` + +## Step 4 (optional) - Deploy the token authority + +The token authority is a contract that glues the token and the colony together and makes it possible for the colony to manage and move the token. The token authority can be deployed using the `deployAuthority` method on the `Token`. After that, another transaction is needed to set the token's `authority` to the one that was just deployed. If the token does not support the `setAuthority` method, this step should be skipped. + +```typescript +// Deploy TokenAuthority +const [{ tokenAuthorityAddress }] = await token + .deployAuthority([colonyAddress]) + .force(); +// Set the token's authority to the freshly deployed one +await token.setAuthority(tokenAuthorityAddress).force(); +``` + + +## Step 5 - Install the `OneTxPayment` extension + +As mentioned earlier, this step is technically optional as well but if the colony is supposed to be used productively, a form of payment extension is needed. Currently only the `OneTxPayment` extension is supported. Install it like so: + +```typescript +const [{ extensionId, version }] = await colony + .installExtension(SupportedExtension.oneTx) + .force(); +await colony.updateExtensions(); +const [{ user, setTo, role }] = await colony + .setRoles(colony.ext.oneTx.address, [ + ColonyRole.Administration, + ColonyRole.Funding, + ]) + .force(); +``` + +Here we install the extension using the `installExtension` method. This extension is an own contract that was deployed in this transaction. To get its address, we re-initialize the extensions on the colony using `updateExtensions`. After that, `oneTx` will be available on `colony.ext`. +Finally, we assign the **Administration** and **Funding** roles of the colony's `Root` team to the extension that we just deployed. The OneTxPayment extension needs these permissions to function properly. + + +## That's it! + +We have successfully deployed a colony that can be used from the dApp as well. Explore what's possible within a colony using Colony SDK [here](../api/classes/Colony.md). diff --git a/docs/guides/transactions.md b/docs/guides/transactions.md index 7627eea1..47e4766d 100644 --- a/docs/guides/transactions.md +++ b/docs/guides/transactions.md @@ -1,3 +1,9 @@ +--- +description: A guide on how to create transactions within Colony. You can create motions and even gasless MetaTransactions in a very straightforward and concise way. + +sidebar_position: 0 +--- + # How to create transactions Within Colony, there are a few ways to do an action. As a colony is a permissioned contract, not everyone can just do anything they like, users (or contracts) have to have the right permission in the relevant team to do so. diff --git a/docs/start.md b/docs/start.md index 7b64986c..c70fe131 100644 --- a/docs/start.md +++ b/docs/start.md @@ -42,7 +42,7 @@ import { providers } from 'ethers'; import { ColonyNetwork, toEth } from '@colony/sdk'; // If MetaMask is installed there will be an `ethereum` object on the `window` -// NOTE: Make sure MetaMask is connected to Gnosis chain (see https://www.xdaichain.com/for-users/wallets/metamask/metamask-setup) +// NOTE: Make sure MetaMask is connected to Gnosis chain (see https://docs.gnosischain.com/tools/wallets/metamask) const provider = new providers.Web3Provider(window.ethereum); // Get the Colony's XDAI funding in the ROOT pot (id 1) diff --git a/examples/browser/src/index.ts b/examples/browser/src/index.ts index 8d1b6df4..60320ac2 100644 --- a/examples/browser/src/index.ts +++ b/examples/browser/src/index.ts @@ -1,4 +1,6 @@ const addColonyRPC = () => { + // If MetaMask is installed there will be an `ethereum` object on the `window` + // eslint-disable-next-line @typescript-eslint/no-explicit-any (window as any).ethereum.request({ method: 'wallet_addEthereumChain', params: [ diff --git a/examples/browser/src/metamask.ts b/examples/browser/src/metamask.ts index 41440191..5b339789 100644 --- a/examples/browser/src/metamask.ts +++ b/examples/browser/src/metamask.ts @@ -2,6 +2,7 @@ import { providers, Signer } from 'ethers'; import { ColonyNetwork } from '../../../src'; // If MetaMask is installed there will be an `ethereum` object on the `window` +// eslint-disable-next-line @typescript-eslint/no-explicit-any const provider = new providers.Web3Provider((window as any).ethereum); // Get the Colony's XDAI funding in the ROOT pot (id 1) diff --git a/examples/browser/src/motions.ts b/examples/browser/src/motions.ts index 34f88b36..d8fccdeb 100644 --- a/examples/browser/src/motions.ts +++ b/examples/browser/src/motions.ts @@ -10,6 +10,8 @@ import { } from '../../../src'; const { isAddress } = utils; +// If MetaMask is installed there will be an `ethereum` object on the `window` +// eslint-disable-next-line @typescript-eslint/no-explicit-any const provider = new providers.Web3Provider((window as any).ethereum); let currentWalletAddress: string; diff --git a/examples/node/create.ts b/examples/node/create.ts new file mode 100644 index 00000000..2f03b22b --- /dev/null +++ b/examples/node/create.ts @@ -0,0 +1,70 @@ +import { providers, Wallet } from 'ethers'; + +import { ColonyNetwork, ColonyRole, SupportedExtension } from '../../src'; + +const provider = new providers.JsonRpcProvider('https://xdai.colony.io/rpc2/'); + +// Deploy a Colony with everything that's needed +const start = async () => { + const signer = new Wallet(process.env.PRIVATE_KEY as string).connect( + provider, + ); + const colonyNetwork = new ColonyNetwork(signer); + // Create token to be used with Colony + const [{ tokenAddress }] = await colonyNetwork + .deployToken('Test token', 'TOT') + .force(); + console.info('Token address', tokenAddress); + if (!tokenAddress) { + return; + } + // Create actual colony (deploys Colony contract) + const [{ colonyAddress }] = await colonyNetwork + .createColony(tokenAddress, 'createcolonytest3') + .force(); + if (!colonyAddress) { + return; + } + console.info('Colony address', colonyAddress); + // Instantiate colony and token + const colony = await colonyNetwork.getColony(colonyAddress); + const token = await colony.getToken(); + // Deploy TokenAuthority + const [{ tokenAuthorityAddress }] = await token + .deployAuthority([colonyAddress]) + .force(); + if (!tokenAuthorityAddress) { + return; + } + console.info('Token authority address', tokenAuthorityAddress); + // Set the token's authority to the freshly deployed one + await token.setAuthority(tokenAuthorityAddress).force(); + // Install OneTxPayment extension + const [{ extensionId, version }] = await colony + .installExtension(SupportedExtension.oneTx) + .force(); + if (!extensionId || !version) { + return; + } + console.info('ExtensionId', extensionId); + console.info('Extension version', version); + // Intantiate Colony again to update the extensions + await colony.updateExtensions(); + if (!colony.ext.oneTx) { + console.error('Could not instantiate OneTx extension within Colony'); + return; + } + // Give Administration and Funding roles to OneTxPayment extension + const [{ user, setTo, role }] = await colony + .setRoles(colony.ext.oneTx.address, [ + ColonyRole.Administration, + ColonyRole.Funding, + ]) + .force(); + if (!role) { + return; + } + console.info(user, setTo, ColonyRole[role]); +}; + +start(); diff --git a/package-lock.json b/package-lock.json index ea9a6a9c..55d5ed7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.7.0", "license": "GPL-3.0-only", "dependencies": { - "@colony/colony-event-metadata-parser": "^2.0.0-beta.0", + "@colony/colony-event-metadata-parser": "^2.0.0-beta.1", "@colony/colony-js": "^6.0.2", "@urql/core": "^2.5.0", "cross-fetch": "^3.1.5", @@ -32,6 +32,7 @@ "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-tsdoc": "^0.2.17", "ethers": "^5.7.1", "fast-glob": "^3.2.11", "husky": "^7.0.4", @@ -174,9 +175,9 @@ } }, "node_modules/@colony/colony-event-metadata-parser": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@colony/colony-event-metadata-parser/-/colony-event-metadata-parser-2.0.0-beta.0.tgz", - "integrity": "sha512-AnXYKH91VDz1u8jng7ulNeigbliTmV9YLgzhXVowNcUBB4EdolQ45IihCoBOURSblISVYjVB/IiB2mIG0Bj8dA==", + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/@colony/colony-event-metadata-parser/-/colony-event-metadata-parser-2.0.0-beta.1.tgz", + "integrity": "sha512-LVp2MFzu5dUFuAmdX11bZw0ZTOtZK/rB/gaL/kbzR1d4CO9ctBmgQQti95etre4SWl7M79Eesn8sGk9sAWPFkw==", "dependencies": { "lint-staged": "^13.0.3", "react-intl": "^6.0.4", @@ -1255,6 +1256,37 @@ "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3676,6 +3708,16 @@ } } }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -5132,6 +5174,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -8206,9 +8254,9 @@ } }, "@colony/colony-event-metadata-parser": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@colony/colony-event-metadata-parser/-/colony-event-metadata-parser-2.0.0-beta.0.tgz", - "integrity": "sha512-AnXYKH91VDz1u8jng7ulNeigbliTmV9YLgzhXVowNcUBB4EdolQ45IihCoBOURSblISVYjVB/IiB2mIG0Bj8dA==", + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/@colony/colony-event-metadata-parser/-/colony-event-metadata-parser-2.0.0-beta.1.tgz", + "integrity": "sha512-LVp2MFzu5dUFuAmdX11bZw0ZTOtZK/rB/gaL/kbzR1d4CO9ctBmgQQti95etre4SWl7M79Eesn8sGk9sAWPFkw==", "requires": { "lint-staged": "^13.0.3", "react-intl": "^6.0.4", @@ -8895,6 +8943,36 @@ "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, + "@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + }, + "dependencies": { + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -10581,6 +10659,16 @@ "prettier-linter-helpers": "^1.0.0" } }, + "eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -11600,6 +11688,12 @@ "iterate-iterator": "^1.0.1" } }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, "js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", diff --git a/package.json b/package.json index 5073a127..79f45025 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.7.0", "license": "GPL-3.0-only", "dependencies": { - "@colony/colony-event-metadata-parser": "^2.0.0-beta.0", + "@colony/colony-event-metadata-parser": "^2.0.0-beta.1", "@colony/colony-js": "^6.0.2", "@urql/core": "^2.5.0", "cross-fetch": "^3.1.5", @@ -26,6 +26,7 @@ "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-tsdoc": "^0.2.17", "ethers": "^5.7.1", "fast-glob": "^3.2.11", "husky": "^7.0.4", diff --git a/src/ColonyNetwork/Colony.ts b/src/ColonyNetwork/Colony.ts index a1d37fb5..cc73c616 100644 --- a/src/ColonyNetwork/Colony.ts +++ b/src/ColonyNetwork/Colony.ts @@ -6,6 +6,8 @@ import { IBasicMetaTransaction, getChildIndex, getPermissionProofs, + isExtensionCompatible, + getExtensionHash, } from '@colony/colony-js'; import { AnnotationEventObject, @@ -13,41 +15,71 @@ import { ColonyFundsClaimed_address_uint256_uint256_EventObject, // eslint-disable-next-line max-len ColonyFundsMovedBetweenFundingPots_address_uint256_uint256_uint256_address_EventObject, + ColonyRoleSet_address_address_uint256_uint8_bool_EventObject, DomainAdded_uint256_EventObject, DomainDeprecatedEventObject, DomainMetadataEventObject, + ExtensionInstalledEventObject, FundingPotAddedEventObject, + RecoveryRoleSetEventObject, } from '@colony/colony-js/extras'; import { AnnotationMetadata, DomainMetadata, MetadataType, } from '@colony/colony-event-metadata-parser'; -import { BigNumberish, BytesLike, ContractReceipt } from 'ethers'; +import { + BigNumber, + BigNumberish, + BytesLike, + ContractReceipt, + utils, +} from 'ethers'; + +import type { Expand, Parameters, ParametersFrom2 } from '../types'; -import { extractEvent } from '../utils'; +import { extractEvent, extractCustomEvent } from '../utils'; import { ColonyToken } from './ColonyToken'; import { ColonyNetwork } from './ColonyNetwork'; -import { OneTxPayment } from './OneTxPayment'; -import { VotingReputation } from './VotingReputation'; -import { Expand, Parameters, ParametersFrom2 } from '../types'; +import { getOneTxPaymentClient, OneTxPayment } from './OneTxPayment'; +import { + getVotingReputationClient, + VotingReputation, +} from './VotingReputation'; import { PermissionConfig, TxCreator } from './TxCreator'; export type SupportedColonyClient = ColonyClientV10; export type SupportedColonyMethods = SupportedColonyClient['functions']; + +/** Extensions that are supported by Colony SDK */ +export enum SupportedExtension { + /** Motions & Disputes (VotingReputation) */ + motion = 'motion', + /** One Transaction Payment (OneTxPayment - enabled by default in Dapp created colonies) */ + oneTx = 'oneTx', +} + +const supportedExtensionMap = { + [SupportedExtension.motion]: VotingReputation, + [SupportedExtension.oneTx]: OneTxPayment, +} as const; + export interface SupportedExtensions { motions?: VotingReputation; oneTx?: OneTxPayment; } export class Colony { - /** The currently supported Colony version. If a Colony is not on this version it has to be upgraded. + /** + * The currently supported Colony version. If a Colony is not on this version it has to be upgraded. * If this is not an option, Colony SDK might throw errors at certain points. Usage of ColonyJS is advised in these cases */ - static SupportedVersions: 10[] = [10]; + static supportedVersions: 10[] = [10]; private colonyClient: SupportedColonyClient; + signerOrProvider: SignerOrProvider; + address: string; colonyNetwork: ColonyNetwork; @@ -61,19 +93,17 @@ export class Colony { ext: SupportedExtensions; - signerOrProvider: SignerOrProvider; - version: number; /** * Creates a new instance to connect to an existing Colony + * * @internal * * @remarks * Do not use this method directly but use [[ColonyNetwork.getColony]] - * - * @param colonyNetwork A Colony SDK `ColonyNetwork` instance - * @param colonyClient A ColonyJS `ColonyClient` in the latest supported version + * @param colonyNetwork - A Colony SDK `ColonyNetwork` instance + * @param colonyClient - A ColonyJS `ColonyClient` in the latest supported version * @returns A [[Colony]] abstaction instance */ constructor( @@ -88,18 +118,30 @@ export class Colony { this.version = colonyClient.clientVersion; } + static async init( + colonyNetwork: ColonyNetwork, + colonyClient: SupportedColonyClient, + ) { + const colony = new Colony(colonyNetwork, colonyClient); + await colony.updateExtensions(); + return colony; + } + + static getLatestSupportedVersion() { + return Colony.supportedVersions[Colony.supportedVersions.length - 1]; + } + /** * Creates a new [[TxCreator]] for non-permissioned Colony transactions - * @internal * + * @internal * @remarks * Do not use this method directly but rather the class methods in the Colony or extensions - * - * @param contract A ColonyJS contract - * @param method The transaction method to execute on the contract - * @param args The arguments for the method - * @param eventData A function that extracts the relevant event data from the [[ContractReceipt]] - * @param metadataType The [[MetadataType]] if the event contains metadata + * @param contract - A ColonyJS contract + * @param method - The transaction method to execute on the contract + * @param args - The arguments for the method + * @param eventData - A function that extracts the relevant event data from the [[ContractReceipt]] + * @param metadataType - The [[MetadataType]] if the event contains metadata * @returns A [[TxCreator]] */ createTxCreator< @@ -118,6 +160,7 @@ export class Colony { ) { return new TxCreator({ colony: this, + colonyNetwork: this.colonyNetwork, contract, method, args, @@ -128,17 +171,16 @@ export class Colony { /** * Creates a new [[TxCreator]] for permissioned Colony transactions - * @internal * + * @internal * @remarks * Do not use this method directly but rather the class methods in the Colony or extensions - * - * @param contract A ColonyJS contract - * @param method The transaction method to execute on the contract - * @param args The arguments for the method - * @param permissionConfig Relevant configuration for the permissioned Colony function - * @param eventData A function that extracts the relevant event data from the [[ContractReceipt]] - * @param metadataType The [[MetadataType]] if the event contains metadata + * @param contract - A ColonyJS contract + * @param method - The transaction method to execute on the contract + * @param args - The arguments for the method + * @param permissionConfig - Relevant configuration for the permissioned Colony function + * @param eventData - A function that extracts the relevant event data from the [[ContractReceipt]] + * @param metadataType - The [[MetadataType]] if the event contains metadata * @returns A permissioned [[TxCreator]] */ createPermissionedTxCreator< @@ -158,6 +200,7 @@ export class Colony { ) { return new TxCreator({ colony: this, + colonyNetwork: this.colonyNetwork, contract, method, args, @@ -169,8 +212,8 @@ export class Colony { /** * Provide direct access to the internally used ColonyJS client. Only use when you know what you're doing - * @internal * + * @internal * @returns The internally used ColonyClient */ getInternalColonyClient(): SupportedColonyClient { @@ -179,8 +222,8 @@ export class Colony { /** * Gets the colony's [[ColonyToken]]. Will instantiate it if it doesn't exist yet - * @internal * + * @internal * @returns The colony's [[ColonyToken]] */ async getToken(): Promise { @@ -196,11 +239,34 @@ export class Colony { } /** - * Installs colony extensions that can be instantiated in the callback function - * @internal + * Refresh colony extensions + * + * Call this function after a new extension was installed. + * It will then become available under `colony.ext` */ - installExtensions(extensions: SupportedExtensions) { - this.ext = extensions; + async updateExtensions() { + // NOTE: Create an individual try-catch block for every extension + if (!this.ext.motions) { + try { + const votingReputationClient = await getVotingReputationClient( + this.colonyClient, + ); + this.ext.motions = new VotingReputation(this, votingReputationClient); + } catch (e) { + // Ignore error here. Extension just won't be available. + } + } + + if (!this.ext.oneTx) { + try { + const oneTxPaymentClient = await getOneTxPaymentClient( + this.colonyClient, + ); + this.ext.oneTx = new OneTxPayment(this, oneTxPaymentClient); + } catch (e) { + // Ignore error here. Extension just won't be available. + } + } } /** @@ -208,7 +274,6 @@ export class Colony { * * @remarks * The function will automatically figure out the corresponding pot for the given domain, as this is what's usually expected. - * * @example * Get the xDAI balance of the team number 2 * ```typescript @@ -219,9 +284,8 @@ export class Colony { * // This will format the balance as a string in eth and not wei (i.e. 1.0 vs. 1000000000000000000) * console.info(toEth(balance)); * ``` - * - * @param tokenAddress The address of the token to get the balance for. Default is the Colony's native token - * @param teamId The teamId (domainId) of the team to get the balance for. Default is the `Root` team + * @param tokenAddress - The address of the token to get the balance for. Default is the Colony's native token + * @param teamId - The teamId (domainId) of the team to get the balance for. Default is the `Root` team * @returns A token balance in [wei](https://gwei.io/) */ async getBalance(tokenAddress?: string, teamId?: BigNumberish) { @@ -239,7 +303,6 @@ export class Colony { * * @remarks * Currently you can only add domains within the `Root` domain. This restriction will be lifted soon - * * @example * ```typescript * import { TeamColor } from '@colony/sdk'; @@ -256,9 +319,7 @@ export class Colony { * }).force(); * })(); * ``` - * - * @param metadata The team metadata you would like to add (or an IPFS CID pointing to valid metadata). If [[DomainMetadata]] is provided directly (as opposed to a [CID](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) for a JSON file) this requires an [[IpfsAdapter]] that can upload and pin to IPFS (like the [[PinataAdapter]]). See its documentation for more information. - * + * @param metadata - The team metadata you would like to add (or an IPFS CID pointing to valid metadata). If [[DomainMetadata]] is provided directly (as opposed to a [CID](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) for a JSON file) this requires an [[IpfsAdapter]] that can upload and pin to IPFS (like the [[PinataAdapter]]). See its documentation for more information. * @returns A [[TxCreator]] * * **Event data** @@ -295,7 +356,6 @@ export class Colony { * * @remarks * Currently you can only add domains within the `Root` domain. This restriction will be lifted soon - * * @returns A [[TxCreator]] * * **Event data** @@ -375,9 +435,8 @@ export class Colony { * * Teams can be deprecated which will remove them from the UI. As they can't be deleted you can always undeprecate a team by passing `false` as the second argument. * - * @param teamId Team to be (un)deprecated - * @param deprecated `true`: Deprecate team; `false`: Undeprecate team - * + * @param teamId - Team to be (un)deprecated + * @param deprecated - `true`: Deprecate team; `false`: Undeprecate team * @returns A [[TxCreator]] * * **Event data** @@ -410,9 +469,7 @@ export class Colony { * Gets the team for `teamId` * * @remarks Will throw if teamId does not exist - * - * @param teamId The teamId to get the team information for - * + * @param teamId - The teamId to get the team information for * @returns A Team object */ async getTeam( @@ -431,9 +488,7 @@ export class Colony { * Anyone can call this function. Claims funds _for_ the Colony that have been sent to the Colony's contract address or minted funds of the Colony's native token. This function _has_ to be called in order for the funds to appear in the Colony's treasury. You can provide a token address for the token to be claimed. Otherwise it will claim the outstanding funds of the Colony's native token * * @remarks use `ethers.constants.AddressZero` to claim ETH. - * - * @param tokenAddress The address of the token to claim the funds for. Default is the Colony's native token - * + * @param tokenAddress - The address of the token to claim the funds for. Default is the Colony's native token * @returns A [[TxCreator]] * * **Event data** @@ -467,7 +522,6 @@ export class Colony { * After sending funds to and claiming funds for your Colony they will land in a special team, the root team. If you want to make payments from other teams (in order to award reputation in that team) you have to move the funds there first. Use this method to do so. * * @remarks Requires the `Funding` permission in the root team. As soon as teams can be nested, this requires the `Funding` permission in a team that is a parent of both teams in which funds are moved. - * * @example * ```typescript * import { Tokens, w } from '@colony/sdk'; @@ -483,11 +537,10 @@ export class Colony { * ).force(); * })(); * ``` - * - * @param amount Amount to transfer between the teams - * @param toTeam The team to transfer the funds to - * @param fromTeam The team to transfer the funds from. Default is the Root team - * @param tokenAddress The address of the token to be transferred. Default is the Colony's native token + * @param amount - Amount to transfer between the teams + * @param toTeam - The team to transfer the funds to + * @param fromTeam - The team to transfer the funds from. Default is the Root team + * @param tokenAddress - The address of the token to be transferred. Default is the Colony's native token * @returns A [[TxCreator]] * * **Event data** @@ -577,8 +630,8 @@ export class Colony { /** * Get the reputation for a user address within a team in the Colony * - * @param userAddress The address of the account to check the reputation for - * @param teamId The teamId (domainId) of the team to get the reputation for. Default is the `Root` team + * @param userAddress - The address of the account to check the reputation for + * @param teamId - The teamId (domainId) of the team to get the reputation for. Default is the `Root` team * @returns A number quantifying the user addresses' reputation */ async getReputation(userAddress: string, teamId = Id.RootDomain) { @@ -591,7 +644,7 @@ export class Colony { /** * Get the reputation for a user address across all teams in the Colony * - * @param userAddress The address of the account to check the reputation for + * @param userAddress - The address of the account to check the reputation for * @returns An array of objects containing the following * * | Property | Description | @@ -630,9 +683,8 @@ export class Colony { * ).force(); * })(); * ``` - * - * @param target Address of the contract to execute a method on - * @param action Encoded action to execute + * @param target - Address of the contract to execute a method on + * @param action - Encoded action to execute * @returns A [[TxCreator]] * * **No event data** @@ -651,28 +703,26 @@ export class Colony { * This will annotate a transaction with an arbitrary text message. This only really works for transactions that happened within this Colony. This will upload the text string to IPFS and connect the transaction to the IPFS hash accordingly. * * @remarks If [[AnnotationMetadata]] is provided directly (as opposed to a [CID](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) for a JSON file) this requires an [[IpfsAdapter]] that can upload and pin to IPFS. See its documentation for more information. Keep in mind that **the annotation itself is a transaction**. - * * @example * ```typescript * // Immediately executing async function * (async function() { * * // Create a motion to pay 10 of the native token to some (maybe your own?) address - * // (forced transaction example) * const [, { transactionHash }] = await colony.ext.oneTx.pay( * '0xb77D57F4959eAfA0339424b83FcFaf9c15407461', * w`10`, * ).motion(); * // Annotate the motion transaction with a little explanation :) + * // (forced transaction example) * await colony.annotateTransaction( * transactionHash, * { annotationMsg: 'I am creating this motion because I think I deserve a little bonus' }, * ).force(); * })(); * ``` - * - * @param txHash Transaction hash of the transaction to annotate (within the Colony) - * @param annotationMetadata The annotation metadata you would like to annotate the transaction with (or an IPFS CID pointing to valid metadata) + * @param txHash - Transaction hash of the transaction to annotate (within the Colony) + * @param annotationMetadata - The annotation metadata you would like to annotate the transaction with (or an IPFS CID pointing to valid metadata) * @returns A [[TxCreator]] * * **Event data** @@ -708,4 +758,194 @@ export class Colony { MetadataType.Annotation, ); } + + /** + * Install an extension for a colony + * + * Valid extensions can be found here: [[SupportedExtension]] + * + * @remarks + * Be aware that some extensions need some extra setup steps (like the `initialise` method on `VotingReputation`). + * After an extension was installed, `colony.updateExtensions()` needs to be called (see example) + * @example + * ```typescript + * // Immediately executing async function + * (async function() { + * // Install the OneTxPayment extension for Colony + * // (forced transaction example) + * await colony.installExtension( + * SupportedExtension.oneTx, + * ).force(); + * // Update the extensions in the colony + * await colony.updateExtensions(); + * console.info(colony.ext.oneTx.address); + * })(); + * ``` + * @param extension - Name of the extension you'd like to install + * @returns A [[TxCreator]] + * + * **Event data** + * + * | Property | Type | Description | + * | :------ | :------ | :------ | + * | `extensionId` | string | Id (name) of the extension (e.g. `OneTxPayment`) | + * | `colony` | string | The address of the colony on which the extension was installed | + * | `version` | BigNumber | The version of the extension that was installed | + */ + installExtension(extension: SupportedExtension) { + const Extension = supportedExtensionMap[extension]; + const version = Extension.getLatestSupportedVersion(); + const { extensionType } = Extension; + const colonyVersion = this.colonyClient.clientVersion; + if (!isExtensionCompatible(extensionType, version, colonyVersion)) { + throw new Error( + `v${version} of ${extensionType} extension is not compatible with colony v${colonyVersion}`, + ); + } + return this.createTxCreator( + this.colonyClient, + 'installExtension', + [getExtensionHash(extensionType), Extension.getLatestSupportedVersion()], + async (receipt) => ({ + ...extractCustomEvent( + 'ExtensionInstalled', + receipt, + this.colonyNetwork.networkClient.interface, + ), + }), + ); + } + + /** + * Set (award) roles to a user/contract + * + * @remarks + * Existing roles will be kept. Use [[unsetRoles]] to remove roles + * @example + * ```typescript + * import { ColonyRole } from '@colony/sdk'; + * + * // Immediately executing async function + * (async function() { + * // Give Administration and Root role to address 0xb794f5ea0ba39494ce839613fffba74279579268 (in Root team) + * // (forced transaction example) + * await colony.setRoles( + * '0xb794f5ea0ba39494ce839613fffba74279579268', + * [ColonyRole.Administration, ColonyRole.Root], + * ).force(); + * })(); + * ``` + * @param address - Address of the wallet or contract to give the roles to + * @param roles - Role or array of roles to award + * @param teamId - Team to apply the role(s) in + * @returns A [[TxCreator]] + * + * **Event data** + * Heads up!* This event is emitted for every role that was set + * + * | Property | Type | Description | + * | :------ | :------ | :------ | + * | `agent` | string | The address that is responsible for triggering this event | + * | `user` | string | Address of the user who was awarded the role | + * | `domainId` | BigNumber | The team the role was awarded for | + * | `role` | number | The number of the role that was awarded. Use `ColonyRole[role]` to get the title of the role | + * | `setTo` | number | Whether the role was awarded or removed | + */ + setRoles( + address: string, + roles: ColonyRole | ColonyRole[], + teamId: BigNumberish = Id.RootDomain, + ) { + return this.createPermissionedTxCreator( + this.colonyClient, + 'setUserRoles', + async () => { + const oldRolesHex = await this.colonyClient.getUserRoles( + address, + teamId, + ); + const oldRoles = parseInt(oldRolesHex, 16); + // TODO: export the colonyRoles2Hex helper from ColonyJS + /* eslint-disable no-bitwise */ + const newRoles = + ([] as ColonyRole[]) + .concat(roles) + .reduce((acc, current) => acc | (1 << current), 0) | oldRoles; + /* eslint-enable no-bitwise */ + const hexRoles = utils.hexZeroPad(`0x${newRoles}`, 32); + return [address, teamId, hexRoles] as [string, BigNumber, string]; + }, + { + roles: ColonyRole.Architecture, + domain: teamId, + }, + async (receipt) => ({ + // eslint-disable-next-line max-len + ...extractEvent( + 'ColonyRoleSet', + receipt, + ), + ...extractEvent('RecoveryRoleSet', receipt), + }), + ); + } + + /** + * Unset (remove) roles from a user/contract + * + * @param address - Address of the wallet or contract to remove the roles from + * @param roles - Role or array of roles to remove + * @param teamId - Team to apply the role(s) in + * @returns A [[TxCreator]] + * + * **Event data** + * Heads up!* This event is emitted for every role that was unset + * + * | Property | Type | Description | + * | :------ | :------ | :------ | + * | `agent` | string | The address that is responsible for triggering this event | + * | `user` | string | Address of the user of which the role was removed | + * | `domainId` | BigNumber | The team the role was removed for | + * | `role` | number | The number of the role that was removed. Use `ColonyRole[role]` to get the title of the role | + * | `setTo` | number | Whether the role was awarded or removed | + */ + unsetRoles( + address: string, + roles: ColonyRole | ColonyRole[], + teamId: BigNumberish = Id.RootDomain, + ) { + return this.createPermissionedTxCreator( + this.colonyClient, + 'setUserRoles', + async () => { + const oldRolesHex = await this.colonyClient.getUserRoles( + address, + teamId, + ); + const oldRoles = parseInt(oldRolesHex, 16); + // TODO: export the colonyRoles2Hex helper from ColonyJS + /* eslint-disable no-bitwise */ + const newRoles = + ([] as ColonyRole[]) + .concat(roles) + .reduce((acc, current) => acc & ~(1 << current), 0b11111) & + oldRoles; + /* eslint-enable no-bitwise */ + const hexRoles = utils.hexZeroPad(`0x${newRoles}`, 32); + return [address, teamId, hexRoles] as [string, BigNumber, string]; + }, + { + roles: ColonyRole.Architecture, + domain: teamId, + }, + async (receipt) => ({ + // eslint-disable-next-line max-len + ...extractEvent( + 'ColonyRoleSet', + receipt, + ), + ...extractEvent('RecoveryRoleSet', receipt), + }), + ); + } } diff --git a/src/ColonyNetwork/ColonyNetwork.ts b/src/ColonyNetwork/ColonyNetwork.ts index 4fbe5544..f150dacd 100644 --- a/src/ColonyNetwork/ColonyNetwork.ts +++ b/src/ColonyNetwork/ColonyNetwork.ts @@ -1,19 +1,31 @@ +import { BigNumberish, ContractReceipt, constants, utils } from 'ethers'; import { ColonyNetworkClient, getColonyNetworkClient, + IBasicMetaTransaction, Network, NetworkClientOptions, SignerOrProvider, } from '@colony/colony-js'; +import { + ColonyAddedEventObject, + ColonyMetadataEventObject, + TokenDeployedEventObject, +} from '@colony/colony-js/extras'; +import { + ColonyMetadata, + MetadataType, +} from '@colony/colony-event-metadata-parser'; import { IpfsMetadata, IpfsAdapter } from '../ipfs'; -import { Colony, SupportedExtensions } from './Colony'; -import { - getVotingReputationClient, - VotingReputation, -} from './VotingReputation'; -import { getOneTxPaymentClient, OneTxPayment } from './OneTxPayment'; -import { MetaTxBroadCasterEndpoint } from '../constants'; +import { Colony } from './Colony'; +import { ColonyLabelSuffix, MetaTxBroadCasterEndpoint } from '../constants'; +import { Expand, Parameters } from '../types'; +import { TxCreator } from './TxCreator'; +import { extractEvent } from '../utils'; + +const { namehash } = utils; +const { AddressZero } = constants; /** Additional options for the [[ColonyNetwork]] */ export interface ColonyNetworkOptions { @@ -30,7 +42,7 @@ export interface ColonyNetworkConfig { } export class ColonyNetwork { - private signerOrProvider: SignerOrProvider; + config: ColonyNetworkConfig; ipfs: IpfsMetadata; @@ -38,7 +50,7 @@ export class ColonyNetwork { networkClient: ColonyNetworkClient; - config: ColonyNetworkConfig; + signerOrProvider: SignerOrProvider; /** * Creates a new instance to connect to the ColonyNetwork @@ -58,8 +70,8 @@ export class ColonyNetwork { * // Now you could call functions on the colonyNetwork, like `colonyNetwork.getMetaColony()` * ``` * - * @param signerOrProvider An _ethers_ compatible Signer or Provider instance - * @param options Optional custom [[ColonyNetworkOptions]] + * @param signerOrProvider - An _ethers_ compatible Signer or Provider instance + * @param options - Optional custom [[ColonyNetworkOptions]] * @returns A ColonyNetwork abstraction instance */ constructor( @@ -83,6 +95,201 @@ export class ColonyNetwork { this.signerOrProvider = signerOrProvider; } + /** + * Creates a new [[TxCreator]] ColonyNetwork transactions + * @internal + * + * @remarks + * Do not use this method directly but rather the class methods in the Colony or extensions + * + * @param contract - A ColonyJS contract + * @param method - The transaction method to execute on the contract + * @param args - The arguments for the method + * @param eventData - A function that extracts the relevant event data from the [[ContractReceipt]] + * @param metadataType - The [[MetadataType]] if the event contains metadata + * @returns A [[TxCreator]] + */ + createTxCreator< + C extends IBasicMetaTransaction, + F extends keyof C['functions'], + D extends Record, + M extends MetadataType, + >( + contract: C, + method: F, + args: + | Parameters + | (() => Promise>), + eventData?: (receipt: ContractReceipt) => Promise, + metadataType?: M, + ) { + return new TxCreator({ + colonyNetwork: this, + contract, + method, + args, + eventData, + metadataType, + }); + } + + /** + * Create a new Colony with metadata + * + * Creates a new Colony with IPFS metadata. To edit metadata at a later point you can call the [[Colony.editColony]] method. + * + * @remarks + * There is more to creating a fully functional colony that can be used within the dapp than just calling this function. See the [Colony Creation Guide](../../guides/colony-creation.md). + * + * @example + * ```typescript + * import { Tokens } from '@colony/sdk'; + * + * // Immediately executing async function + * (async function() { + * // Create a colony with some metadata details attached + * // (forced transaction example) + * // (also notice that this requires an upload-capable IPFS adapter) + * await colonyNetwork.createColony( + * // Use USDC on Gnosis chain as the native token + * '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83', { + * colonyDisplayName: 'Cool Colony', + * // IPFS hash to an image file + * colonyAvatarHash: 'QmS26o1Cmsrx7iw1SSFGEcy22TVDq6VmEZ4XNjpWFyaKUe', + * // List of token addresses that the Colony should be initialized with (can be changed later) - excluding ETH and the native token from above + * colonyTokens: [Tokens.CLNY], + * }).force(); + * })(); + * ``` + * + * @param metadata - The team metadata you would like to add (or an IPFS CID pointing to valid metadata). If [[ColonyMetadata]] is provided directly (as opposed to a [CID](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) for a JSON file) this requires an [[IpfsAdapter]] that can upload and pin to IPFS (like the [[PinataAdapter]]). See its documentation for more information. + * + * @returns A [[TxCreator]] + * + * **Event data** + * + * | Property | Type | Description | + * | :------ | :------ | :------ | + * | `colonyId` | BigNumber | Auto-incremented integer id of the colony | + * | `colonyAddress` | string | Address of the newly deployed colony contract | + * | `token` | string | Address of the token that is used as the colony's native token | + * | `metadata` | string | IPFS CID of metadata attached to this transaction | + * + * **Metadata** (can be obtained by calling and awaiting the `getMetadata` function) + * + * | Property | Type | Description | + * | :------ | :------ | :------ | + * | `colonyDisplayName` | string | The name that should be displayed for the colony | + * | `colonyAvatarHash` | string | An IPFS hash for a Colony logo (make it 200x200px) | + * | `colonyTokens` | string[] | A list of additional tokens that should be in the colony's "address book" | + */ + createColony( + tokenAddress: string, + label: string, + metadata: ColonyMetadata | string, + ): TxCreator< + ColonyNetworkClient, + 'createColony(address,uint256,string,string)', + Expand, + MetadataType.Colony + >; + + /** + * Create a new Colony without metadata + * + * Creates a new Colony without IPFS metadata. To add metadata at a later point you can call the [[Colony.editColony]] method. + * + * @remarks + * There is more to creating a fully functional colony that can be used within the dapp than just calling this function. See the [Colony Creation Guide](../../guides/colony-creation.md). + * + * @example + * ```typescript + * // Immediately executing async function + * (async function() { + * // Create a colony + * // (forced transaction example) + * await colonyNetwork + * // Use USDC on Gnosis chain as the native token + * .createColony('0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83') + * .force(); + * })(); + * ``` + * + * @returns A [[TxCreator]] + * + * **Event data** + * + * | Property | Type | Description | + * | :------ | :------ | :------ | + * | `colonyId` | BigNumber | Auto-incremented integer id of the colony | + * | `colonyAddress` | string | Address of the newly deployed colony contract | + * | `token` | string | Address of the token that is used as the colony's native token | + */ + createColony( + tokenAddress: string, + label: string, + ): TxCreator< + ColonyNetworkClient, + 'createColony(address,uint256,string)', + Expand, + MetadataType + >; + + createColony( + tokenAddress: string, + label: string, + metadata?: ColonyMetadata | string, + ) { + const checkLabel = async () => { + const existingLabel = await this.getColonyAddress(label); + + if (existingLabel) { + throw new Error(`Colony label ${label} already exists`); + } + return [tokenAddress, Colony.getLatestSupportedVersion(), label] as [ + string, + BigNumberish, + string, + ]; + }; + + if (!metadata) { + return this.createTxCreator( + this.networkClient, + 'createColony(address,uint256,string)', + checkLabel, + async (receipt) => ({ + ...extractEvent('ColonyAdded', receipt), + }), + ); + } + + return this.createTxCreator( + this.networkClient, + 'createColony(address,uint256,string,string)', + async () => { + await checkLabel(); + + let cid: string; + if (typeof metadata == 'string') { + cid = metadata; + } else { + cid = await this.ipfs.uploadMetadata(MetadataType.Colony, metadata); + } + return [ + tokenAddress, + Colony.getLatestSupportedVersion(), + label, + cid, + ] as [string, BigNumberish, string, string]; + }, + async (receipt) => ({ + ...extractEvent('ColonyAdded', receipt), + }), + MetadataType.Colony, + ); + } + /** * Get a new instance of a Colony * @@ -91,42 +298,19 @@ export class ColonyNetwork { * @remarks * Colony contracts are versioned. If the deployed Colony version does not match the supported version an error will be thrown * - * @param address The Colony's address + * @param address - The Colony's address * @returns A Colony abstaction instance */ async getColony(address: string): Promise { const colonyClient = await this.networkClient.getColonyClient(address); - if (colonyClient.clientVersion !== Colony.SupportedVersions[0]) { + if (colonyClient.clientVersion !== Colony.supportedVersions[0]) { throw new Error( `The version of this Colony ${colonyClient.clientVersion} is not supported by Colony SDK. Please update your Colony`, ); } - const colony = new Colony(this, colonyClient); - - const extensions: SupportedExtensions = {}; - - // NOTE: Create an individual try-catch block for every extension - try { - const votingReputationClient = await getVotingReputationClient( - colonyClient, - ); - extensions.motions = new VotingReputation(colony, votingReputationClient); - } catch (e) { - // Ignore error here. Extension just won't be available. - } - - try { - const oneTxPaymentClient = await getOneTxPaymentClient(colonyClient); - extensions.oneTx = new OneTxPayment(colony, oneTxPaymentClient); - } catch (e) { - // Ignore error here. Extension just won't be available. - } - - colony.installExtensions(extensions); - - return colony; + return Colony.init(this, colonyClient); } /** @@ -140,4 +324,60 @@ export class ColonyNetwork { const colonyAddress = await this.networkClient.getMetaColony(); return this.getColony(colonyAddress); } + + /** + * Get the colony's ENS label + * + * Returns the colony's ENS label, just like it's shown in the browsers address bar after `/colony/`, when using the dApp. + * Will return `null` if the colony does not exist or if no label was assigned yet + * + * @returns The colony's ENS label + */ + async getColonyLabel(address: string) { + const ensName = await this.networkClient.lookupRegisteredENSDomain(address); + if (ensName) { + return ensName.replace(ColonyLabelSuffix[this.network], ''); + } + return null; + } + + /** + * Get the colony's addess by the ENS label + * + * Returns the colony's address that belongs to the given ENS label + * Will return `null` if the given label was not assigned to a colony. + * + * @returns The colony's address + */ + async getColonyAddress(label: string) { + const hash = namehash(`${label}${ColonyLabelSuffix[this.network]}`); + const address = await this.networkClient.addr(hash); + if (address !== AddressZero) { + return address; + } + return null; + } + + /** + * Deploy a "special" colony ERC20 token + * + * If there is not token yet that should be used with the Colony, this is the canonical way to create one. + * + * This is a supercharged ERC20 token contract, that not only has a permissioned `mint` function (that can be used from the colony) but also supports Metatransactions. In order to fully use its permissioned system with a colony, some extra steps have to be taken. See the [Colony Creation Guide](../../guides/colony-creation.md). + * + * @remarks + * The token deployed with this function is locked by default. Call `unlockToken()` on the Colony at a later point to unlock it. + * + * @returns The colony's address + */ + deployToken(name: string, symbol: string, decimals = 18) { + return this.createTxCreator( + this.networkClient, + 'deployTokenViaNetwork', + [name, symbol, decimals], + async (receipt) => ({ + ...extractEvent('TokenDeployed', receipt), + }), + ); + } } diff --git a/src/ColonyNetwork/ColonyToken.ts b/src/ColonyNetwork/ColonyToken.ts index cc04c030..01f541ad 100644 --- a/src/ColonyNetwork/ColonyToken.ts +++ b/src/ColonyNetwork/ColonyToken.ts @@ -5,6 +5,7 @@ import { } from '@colony/colony-js'; import { ApprovalEventObject, + TokenAuthorityDeployedEventObject, UserTokenDepositedEventObject, UserTokenWithdrawnEventObject, } from '@colony/colony-js/extras'; @@ -19,6 +20,8 @@ export class ColonyToken { private tokenClient: ColonyTokenClient; + address: string; + tokenLockingClient: TokenLockingClient; // TODO: Add symbol, decimals @@ -31,7 +34,7 @@ export class ColonyToken { * @remarks * Do not use this method directly but use [[Colony.getToken]] * - * @param colony A Colony instance + * @param colony - A Colony instance * @returns A Colony Token abstaction instance */ constructor(colony: Colony, tokenLockingClient: TokenLockingClient) { @@ -41,11 +44,22 @@ export class ColonyToken { `Requested token is not a token deployed with Colony. Please use the Erc20Token class`, ); } + this.address = colonyClient.tokenClient.address; this.colony = colony; this.tokenClient = colonyClient.tokenClient; this.tokenLockingClient = tokenLockingClient; } + /** + * Provide direct access to the internally used ColonyJS TokenClient client. Only use when you know what you're doing + * @internal + * + * @returns The internally used TokenClient + */ + getInternalTokenClient(): ColonyTokenClient { + return this.tokenClient; + } + /** * Gets the token's symbol * @@ -77,7 +91,7 @@ export class ColonyToken { * })(); * ``` * - * @param amount Amount of the token to be minted + * @param amount - Amount of the token to be minted * * @returns A [[TxCreator]] */ @@ -108,7 +122,7 @@ export class ColonyToken { * })(); * ``` * - * @param amount Amount of the token to be approved + * @param amount - Amount of the token to be approved * * @returns A tupel of event data and contract receipt * @@ -151,7 +165,7 @@ export class ColonyToken { * })(); * ``` * - * @param amount Amount of the token to be deposited + * @param amount - Amount of the token to be deposited * * @returns A tupel of event data and contract receipt * @@ -196,7 +210,7 @@ export class ColonyToken { * })(); * ``` * - * @param amount Amount of the token to be withdrawn + * @param amount - Amount of the token to be withdrawn * * @returns A tupel of event data and contract receipt * @@ -229,7 +243,7 @@ export class ColonyToken { * * This method will show the accumulated amount that was deposited using the [[ColonyToken.deposit]] method * - * @param user The wallet address that we want to check the deposited amount of + * @param user - The wallet address that we want to check the deposited amount of * * @returns The currently deposited balance of the Colony's native token */ @@ -246,8 +260,8 @@ export class ColonyToken { * * This method will show the accumulated amount that was approved using the [[ColonyToken.approve]] method * - * @param user The wallet address that we want to check the approved amount of - * @param obligator The address that has been approved to obligate the funds. + * @param user - The wallet address that we want to check the approved amount of + * @param obligator - The address that has been approved to obligate the funds. * * @returns The currently approved balance of the Colony's native token for the obligator */ @@ -258,4 +272,45 @@ export class ColonyToken { obligator, ); } + + deployAuthority(allowedToTransfer?: string[]) { + const { colonyNetwork } = this.colony; + return colonyNetwork.createTxCreator( + colonyNetwork.networkClient, + 'deployTokenAuthority', + async () => { + let allowed: string[] = []; + const tokenLockingAddress = + await colonyNetwork.networkClient.getTokenLocking(); + if (!allowedToTransfer) { + allowed = [tokenLockingAddress]; + } else { + allowed = [...allowedToTransfer, tokenLockingAddress]; + } + return [this.address, this.colony.address, allowed] as [ + string, + string, + string[], + ]; + }, + async (receipt) => ({ + ...extractEvent( + 'TokenAuthorityDeployed', + receipt, + ), + }), + ); + } + + setAuthority(tokenAuthorityAddress: string) { + return this.colony.createTxCreator(this.tokenClient, 'setAuthority', [ + tokenAuthorityAddress, + ]); + } + + setupColonyAsOwner() { + return this.colony.createTxCreator(this.tokenClient, 'setOwner', [ + this.colony.address, + ]); + } } diff --git a/src/ColonyNetwork/OneTxPayment.ts b/src/ColonyNetwork/OneTxPayment.ts index 879d744b..c5327f41 100644 --- a/src/ColonyNetwork/OneTxPayment.ts +++ b/src/ColonyNetwork/OneTxPayment.ts @@ -13,16 +13,19 @@ import { extractEvent } from '../utils'; import { Colony, SupportedColonyClient } from './Colony'; export type SupportedOneTxPaymentClient = OneTxPaymentClientV3; -export const SUPPORTED_ONE_TX_PAYMENT_VERSION = 3; export const getOneTxPaymentClient = async ( colonyClient: SupportedColonyClient, ) => { const oneTxPaymentClient = await colonyClient.getExtensionClient( - Extension.OneTxPayment, + OneTxPayment.extensionType, ); - if (oneTxPaymentClient.clientVersion !== SUPPORTED_ONE_TX_PAYMENT_VERSION) { + // TODO: Support more versions? + if ( + oneTxPaymentClient.clientVersion !== + OneTxPayment.getLatestSupportedVersion() + ) { throw new Error( `The installed version ${oneTxPaymentClient.clientVersion} of the OneTxPayment extension is not supported. Please upgrade the extension in your Colony`, ); @@ -45,12 +48,22 @@ export const getOneTxPaymentClient = async ( * Note: if you deployed your Colony using the Dapp, the OneTxPayment extension is already installed for you */ export class OneTxPayment { + static supportedVersion: 3[] = [3]; + + static extensionType: Extension.OneTxPayment = Extension.OneTxPayment; + private colony: Colony; private oneTxPaymentClient: SupportedOneTxPaymentClient; address: string; + static getLatestSupportedVersion() { + return OneTxPayment.supportedVersion[ + OneTxPayment.supportedVersion.length - 1 + ]; + } + constructor(colony: Colony, oneTxPaymentClient: SupportedOneTxPaymentClient) { this.address = oneTxPaymentClient.address; this.colony = colony; @@ -79,10 +92,10 @@ export class OneTxPayment { * })(); * ``` * - * @param recipient Wallet address of account to send the funds to (also awarded reputation when sending the native token) - * @param amount Amount to pay in wei - * @param tokenAddress The address of the token to make the payment in. Default is the Colony's native token - * @param teamId The team to use to send the funds from. Has to have funding of at least the amount you need to send. See [[Colony.moveFundsToTeam]]. Defaults to the Colony's root team + * @param recipient - Wallet address of account to send the funds to (also awarded reputation when sending the native token) + * @param amount - Amount to pay in wei + * @param tokenAddress - The address of the token to make the payment in. Default is the Colony's native token + * @param teamId - The team to use to send the funds from. Has to have funding of at least the amount you need to send. See [[Colony.moveFundsToTeam]]. Defaults to the Colony's root team * @returns A [[TxCreator]] * * **Event data** diff --git a/src/ColonyNetwork/TxCreator.ts b/src/ColonyNetwork/TxCreator.ts index c4ab11cb..f2272ac2 100644 --- a/src/ColonyNetwork/TxCreator.ts +++ b/src/ColonyNetwork/TxCreator.ts @@ -14,13 +14,15 @@ import { utils, } from 'ethers'; import { fetch } from 'cross-fetch'; +import { MetadataType } from '@colony/colony-event-metadata-parser'; import { MotionCreatedEventObject } from '@colony/colony-js/extras'; import { Colony } from './Colony'; -import { MetadataType, MetadataValue } from '../ipfs'; +import { MetadataValue } from '../ipfs'; import { extractEvent } from '../utils'; import { ParsedLogTransactionReceipt } from '../types'; import { IPFS_METADATA_EVENTS } from '../ipfs/IpfsMetadata'; +import { ColonyNetwork } from './ColonyNetwork'; const { arrayify, solidityKeccak256, splitSignature } = utils; @@ -74,7 +76,9 @@ export class TxCreator< E extends EventData, MD extends MetadataType, > { - private colony: Colony; + private colonyNetwork: ColonyNetwork; + + private colony?: Colony; private contract: C; @@ -90,6 +94,7 @@ export class TxCreator< constructor({ colony, + colonyNetwork, contract, method, args, @@ -97,7 +102,8 @@ export class TxCreator< metadataType, permissionConfig, }: { - colony: Colony; + colony?: Colony; + colonyNetwork: ColonyNetwork; contract: C; method: M; args: unknown[] | (() => Promise); @@ -106,6 +112,7 @@ export class TxCreator< permissionConfig?: PermissionConfig; }) { this.colony = colony; + this.colonyNetwork = colonyNetwork; this.contract = contract; this.method = method as string; this.args = args; @@ -124,6 +131,11 @@ export class TxCreator< } if (this.permissionConfig) { + if (!this.colony) { + throw new Error( + 'Permissioned transactions can only be created on a Colony', + ); + } const [permissionDomainId, childSkillIndex] = await getPermissionProofs( this.colony.getInternalColonyClient(), this.permissionConfig.domain, @@ -144,12 +156,11 @@ export class TxCreator< const data = await this.eventData(receipt); if (this.metadataType && data.metadata) { - const getMetadata = - this.colony.colonyNetwork.ipfs.getMetadataForEvent.bind( - this.colony.colonyNetwork.ipfs, - IPFS_METADATA_EVENTS[this.metadataType], - data.metadata, - ) as () => Promise>; + const getMetadata = this.colonyNetwork.ipfs.getMetadataForEvent.bind( + this.colonyNetwork.ipfs, + IPFS_METADATA_EVENTS[this.metadataType], + data.metadata, + ) as () => Promise>; return [data, receipt as R, getMetadata]; } @@ -161,7 +172,7 @@ export class TxCreator< } private getSigner(): Signer { - const { signerOrProvider } = this.colony; + const { signerOrProvider } = this.colonyNetwork; if (!(signerOrProvider instanceof Signer)) { throw new Error('Need a signer to create a transaction'); } @@ -172,11 +183,9 @@ export class TxCreator< encodedTransaction: string, target: string, ): Promise { - const { colonyNetwork } = this.colony; - - if (!colonyNetwork.config.metaTxBroadcasterEndpoint) { + if (!this.colonyNetwork.config.metaTxBroadcasterEndpoint) { throw new Error( - `No metatransaction broadcaster endpoint found for network ${colonyNetwork.network}`, + `No metatransaction broadcaster endpoint found for network ${this.colonyNetwork.network}`, ); } @@ -189,7 +198,7 @@ export class TxCreator< let chainId: number; - if (colonyNetwork.network === Network.Custom) { + if (this.colonyNetwork.network === Network.Custom) { chainId = 1; } else { const networkInfo = await provider.getNetwork(); @@ -219,7 +228,7 @@ export class TxCreator< }; const res = await fetch( - `${colonyNetwork.config.metaTxBroadcasterEndpoint}/broadcast`, + `${this.colonyNetwork.config.metaTxBroadcasterEndpoint}/broadcast`, { method: 'POST', headers: { @@ -280,6 +289,10 @@ export class TxCreator< * @returns A tupel of motion event data and contract receipt */ async motion(motionDomain: BigNumberish = Id.RootDomain) { + if (!this.colony) { + throw new Error('Motions can only be created on a Colony'); + } + if (!this.colony.ext.motions) { throw new Error( 'VotingReputation extension is not installed for this Colony', @@ -337,6 +350,10 @@ export class TxCreator< * @returns A tupel of motion event data and contract receipt */ async motionMeta(motionDomain: BigNumberish = Id.RootDomain) { + if (!this.colony) { + throw new Error('Motions can only be created on a Colony'); + } + if (!this.colony.ext.motions) { throw new Error( 'VotingReputation extension is not installed for this Colony', diff --git a/src/ColonyNetwork/VotingReputation.ts b/src/ColonyNetwork/VotingReputation.ts index 7fb067fd..e554755a 100644 --- a/src/ColonyNetwork/VotingReputation.ts +++ b/src/ColonyNetwork/VotingReputation.ts @@ -21,7 +21,6 @@ import { extractEvent, extractCustomEvent, toEth } from '../utils'; import { Colony, SupportedColonyClient } from './Colony'; export type SupportedVotingReputationClient = VotingReputationClientV7; -export const SUPPORTED_VOTING_REPUTATION_VERSION = 7; export type Motion = VotingReputationDataTypes.MotionStruct; @@ -38,11 +37,12 @@ export const getVotingReputationClient = async ( colonyClient: SupportedColonyClient, ) => { const votingReputationClient = await colonyClient.getExtensionClient( - Extension.VotingReputation, + VotingReputation.extensionType, ); if ( - votingReputationClient.clientVersion !== SUPPORTED_VOTING_REPUTATION_VERSION + votingReputationClient.clientVersion !== + VotingReputation.getLatestSupportedVersion() ) { throw new Error( `The installed version ${votingReputationClient.clientVersion} of the VotingReputation extension is not supported. Please upgrade the extension in your Colony`, @@ -140,12 +140,23 @@ const REP_DIVISOR = BigNumber.from(10).pow(18); * */ export class VotingReputation { + static supportedVersions: 7[] = [7]; + + static extensionType: Extension.IVotingReputation = + Extension.IVotingReputation; + private colony: Colony; private votingReputationClient: SupportedVotingReputationClient; address: string; + static getLatestSupportedVersion() { + return VotingReputation.supportedVersions[ + VotingReputation.supportedVersions.length - 1 + ]; + } + constructor( colony: Colony, votingReputationClient: SupportedVotingReputationClient, @@ -230,9 +241,9 @@ export class VotingReputation { * * @remarks You will usually not use this function directly, but use the `send` or `motion` functions of the [[TxCreator]] within the relevant contract. * - * @param motionDomain The domain the motion will be created in - * @param encodedAction The encoded action as a string - * @param altTarget The contract to which we send the action - 0x0 for the colony (default) + * @param motionDomain - The domain the motion will be created in + * @param encodedAction - The encoded action as a string + * @param altTarget - The contract to which we send the action - 0x0 for the colony (default) * * @returns A Motion object */ @@ -261,7 +272,7 @@ export class VotingReputation { * * @remarks Will throw if motionId does not exist * - * @param motionId The motionId to get the information for + * @param motionId - The motionId to get the information for * * @returns A Motion object */ @@ -300,7 +311,7 @@ export class VotingReputation { * * @remarks Will throw if motionId does not exist * - * @param motionId The motionId to get the state for + * @param motionId - The motionId to get the state for * * @returns The motion state */ @@ -317,8 +328,8 @@ export class VotingReputation { * * @remarks To get the missing amount for activation, call [[getRemainingStakes]] * - * @param motion A Motion struct object - * @param vote A vote for (Yay) or against (Nay) the motion + * @param motion - A Motion struct object + * @param vote - A vote for (Yay) or against (Nay) the motion * * @returns The minimum stake amount */ @@ -378,7 +389,7 @@ export class VotingReputation { /** * Get the amounts remaining for Yay/Nay sides to be activated * - * @param motionId The motionId of the motion + * @param motionId - The motionId of the motion * * @returns An object containing the remaining amounts */ @@ -416,8 +427,8 @@ export class VotingReputation { * Approve `amount` of the "activated" native tokens of a user for staking in a specific team * After a token was "activated" (approved and deposited via the native token interface) it can be used for staking motions. To stake a motion, the token amount for staking has to be approved for the domain the motion was created in. See also the example in [[VotingReputation.stakeMotion]] * - * @param amount Amount of "activated" tokens to be approved for staking - * @param teamId Team that the approved tokens can be used in for staking motions + * @param amount - Amount of "activated" tokens to be approved for staking + * @param teamId - Team that the approved tokens can be used in for staking motions * * @returns A tupel of event data and contract receipt * @@ -475,7 +486,7 @@ export class VotingReputation { * })(); * ``` * - * @param amount Amount of the token to be approved + * @param amount - Amount of the token to be approved * * @returns A tupel of event data and contract receipt * @@ -564,8 +575,8 @@ export class VotingReputation { /** * Submit a vote for a motion * - * @param motionId The motionId of the motion to be finalized - * @param vote A vote for (Yay) or against (Nay) the motion + * @param motionId - The motionId of the motion to be finalized + * @param vote - A vote for (Yay) or against (Nay) the motion * * @returns A tupel of event data and contract receipt * @@ -624,8 +635,8 @@ export class VotingReputation { * * @remarks In order for a vote to count it has to be revealed within the reveal period. Only then rewards can be paid out to the voter. * - * @param motionId The motionId of the motion to be finalized - * @param vote The vote that was cast. If not provided Colony SDK will try to find out which side was voted on (not recommended) + * @param motionId - The motionId of the motion to be finalized + * @param vote - The vote that was cast. If not provided Colony SDK will try to find out which side was voted on (not recommended) * * @returns A tupel of event data and contract receipt * @@ -702,7 +713,7 @@ export class VotingReputation { * - Voting is still in progress * - The motion was already finalized * - * @param motionId The motionId of the motion to be finalized + * @param motionId - The motionId of the motion to be finalized * * @returns A tupel of event data and contract receipt * diff --git a/src/ColonyNetwork/index.ts b/src/ColonyNetwork/index.ts index 6e0cef75..7a8a25b9 100644 --- a/src/ColonyNetwork/index.ts +++ b/src/ColonyNetwork/index.ts @@ -1,10 +1,6 @@ -/** - * This is a description of the ColonyNetwork module - * @module ColonyNetwork - */ - export { ColonyNetwork, ColonyNetworkOptions } from './ColonyNetwork'; -export { Colony, SupportedExtensions } from './Colony'; +export { Colony, SupportedExtension, SupportedExtensions } from './Colony'; export { ColonyToken } from './ColonyToken'; +export { OneTxPayment } from './OneTxPayment'; export { VotingReputation, Vote } from './VotingReputation'; export { TxCreator } from './TxCreator'; diff --git a/src/constants.ts b/src/constants.ts index da608016..af38539b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -34,3 +34,12 @@ export enum TeamColor { Magenta, PurpleGrey, } + +export enum ColonyLabelSuffix { + Mainnet = '.colony.joincolony.eth', + Goerli = '.colony.joincolony.test', + Gnosis = '.colony.joincolony.colonyxdai', + Xdai = '.colony.joincolony.colonyxdai', + XdaiQa = '.colony.joincolony.colonyxdai', + Custom = '.colony.joincolony.test', +} diff --git a/src/events/ColonyEventManager.ts b/src/events/ColonyEventManager.ts index 197ed7d8..6ce07e7e 100644 --- a/src/events/ColonyEventManager.ts +++ b/src/events/ColonyEventManager.ts @@ -1,9 +1,5 @@ -/** - * This is a description of the ColonyEventManager module - * @module ColonyEventManager - */ - import { constants, providers, EventFilter } from 'ethers'; + import type { Result } from 'ethers/lib/utils'; import type { BlockTag } from '@ethersproject/abstract-provider'; import { @@ -16,6 +12,7 @@ import { VotingReputationEvents, VotingReputationEvents__factory, } from '@colony/colony-js/extras'; +import type { MetadataType } from '@colony/colony-event-metadata-parser'; import type { Ethers6Filter } from '../types'; import { addressesAreEqual, getLogs, nonNullable } from '../utils'; @@ -23,7 +20,6 @@ import { IpfsAdapter, IpfsMetadata, MetadataEvent, - MetadataType, MetadataValue, } from '../ipfs'; @@ -99,8 +95,8 @@ export class ColonyEventManager { * As opposed to the [[ColonyNetwork]] class, this constructor _needs_ an _ethers_ JsonRpcProvider (or a subclass of it) as it's * the only provider that supports topic filtering by multiple addresses * - * @param provider An _ethers_ `JsonRpcProvider` - * @param options Optional custom [[ColonyEventManagerOptions]] + * @param provider - An _ethers_ `JsonRpcProvider` + * @param options - Optional custom [[ColonyEventManagerOptions]] * @returns A ColonyEvents instance */ constructor( @@ -165,7 +161,7 @@ export class ColonyEventManager { * })(); * ``` * - * @param filter A [[ColonyFilter]]. [[ColonyMultiFilter]]s will not work + * @param filter - A [[ColonyFilter]]. [[ColonyMultiFilter]]s will not work * @returns An array of [[ColonyEvent]]s */ async getEvents( @@ -234,10 +230,8 @@ export class ColonyEventManager { * })(); * ``` * - * @param filters An array of [[ColonyMultiFilter]]s. Normal [[ColonyFilter]]s will not work - * @param options You can define `fromBlock` and `toBlock` only once for all the filters given - * @param options.fromBlock Starting block in which to look for this event - inclusive (default: 'latest') - * @param options.toBlock Ending block in which to look for this event - inclusive (default: 'latest') + * @param filters - An array of [[ColonyMultiFilter]]s. Normal [[ColonyFilter]]s will not work + * @param options - You can define `fromBlock` and `toBlock` only once for all the filters given (default for both is `latest`) * @returns An array of [[ColonyEvent]]s */ async getMultiEvents( @@ -336,15 +330,15 @@ export class ColonyEventManager { * ); * ``` * - * @typeParam T Needs to be a valid [[EventSource]] (i.e. from `colonyEvents.eventSources`) - * @typeParam N An event signature as defined in the _ethers_ contract's [`filters`](https://docs.ethers.io/v5/api/contract/contract/#Contract--filters) object. + * @typeParam T - Needs to be a valid [[EventSource]] (i.e. from `colonyEvents.eventSources`) + * @typeParam N - An event signature as defined in the _ethers_ contract's [`filters`](https://docs.ethers.io/v5/api/contract/contract/#Contract--filters) object. * See the [ColonyJS documentation](https://colony.gitbook.io/colony/colonyjs) for a list of all available contracts and events * - * @param contract A valid [[EventSource]] - * @param eventName A valid event signature from the contract's `filters` object - * @param address Address of the contract that can emit this event - * @param params Parameters to filter by for the event. Has to be indexed in the contract (see _ethers_ [Event Filters](https://docs.ethers.io/v5/api/contract/contract/#Contract--filters)) - * @param options You can define `fromBlock` and `toBlock` only once for all the filters given + * @param contract - A valid [[EventSource]] + * @param eventName - A valid event signature from the contract's `filters` object + * @param address - Address of the contract that can emit this event + * @param params - Parameters to filter by for the event. Has to be indexed in the contract (see _ethers_ [Event Filters](https://docs.ethers.io/v5/api/contract/contract/#Contract--filters)) + * @param options - You can define `fromBlock` and `toBlock` only once for all the filters given (default for both is `latest`) * @returns A [[ColonyFilter]] */ createFilter< @@ -408,13 +402,13 @@ export class ColonyEventManager { * ); * ``` * - * @typeParam T Needs to be a valid [[EventSource]] (i.e. from `colonyEvents.eventSources`) - * @typeParam N An event signature as defined in the _ethers_ contract's [`filters`](https://docs.ethers.io/v5/api/contract/contract/#Contract--filters) object. + * @typeParam T - Needs to be a valid [[EventSource]] (i.e. from `colonyEvents.eventSources`) + * @typeParam N - An event signature as defined in the _ethers_ contract's [`filters`](https://docs.ethers.io/v5/api/contract/contract/#Contract--filters) object. * See the [ColonyJS documentation](https://colony.gitbook.io/colony/colonyjs) for a list of all available contracts and events * - * @param contract A valid [[EventSource]] - * @param eventNames A list of valid event signatures from the contract's `filters` object - * @param address Address of the contract that can emit this event + * @param contract - A valid [[EventSource]] + * @param eventNames - A list of valid event signatures from the contract's `filters` object + * @param address - Address of the contract that can emit this event * @returns A [[ColonyMultiFilter]] */ createMultiFilter< diff --git a/src/events/index.ts b/src/events/index.ts index 982fc02b..4e23e106 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -1,8 +1,3 @@ -/** - * This is a description of the events module - * @module events - */ - export { ColonyEvent, ColonyEventManager, diff --git a/src/graph/index.ts b/src/graph/index.ts index 4d27f668..b31c7b3d 100644 --- a/src/graph/index.ts +++ b/src/graph/index.ts @@ -26,7 +26,7 @@ export interface SubgraphClientOptions { * * The Colony Subgraph's schema and resolvers are kept up-to-date by the Colony team and can be explored here: [Colony Subgraph](https://thegraph.com/hosted-service/subgraph/joincolony/colony-xdai). There you can make test queries to the Colony Subgraph and explore it all the way down the rabbit hole :) * - * @param options Define configuration options to instantiate the client with + * @param options - Define configuration options to instantiate the client with * @returns A ready-to-use `urql` GraphQL client instance * * @example diff --git a/src/index.ts b/src/index.ts index 96d42949..e82ff415 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,13 @@ export { MotionState, NetworkClientOptions, Tokens } from '@colony/colony-js'; -export type { AnnotationMetadata } from '@colony/colony-event-metadata-parser'; +export type { + AnnotationMetadata, + ColonyMetadata, + DomainMetadata, + MetadataType, +} from '@colony/colony-event-metadata-parser'; export * from './ColonyNetwork'; export * from './events'; -export * from './events'; export * from './ipfs'; export * from './constants'; export * from './utils'; diff --git a/src/ipfs/IpfsAdapter.ts b/src/ipfs/IpfsAdapter.ts index 932aa840..46345f77 100644 --- a/src/ipfs/IpfsAdapter.ts +++ b/src/ipfs/IpfsAdapter.ts @@ -5,7 +5,7 @@ export default interface IpfsAdapter { /** * Should return the whole URL to an IPFS resource on the corresponding gateway (e.g. https://my-ipfs-gateway/ipfs/QmXxxxXXxxXxXxXxxxXXxxxXxXXx). * - * @param cid An IPFS hash (CID) + * @param cid - An IPFS hash (CID) * @returns The URL to an ipfs resource */ getIpfsUrl(cid: string): string; @@ -15,7 +15,7 @@ export default interface IpfsAdapter { * * @remarks This function should ideally **pin** your data on the relevant service. * - * @param jsonString JSON string to upload (and pin) to IPFS + * @param jsonString - JSON string to upload (and pin) to IPFS * @returns Promise to IPFS hash (CID) */ uploadJson(jsonString: string): Promise; diff --git a/src/ipfs/IpfsMetadata.ts b/src/ipfs/IpfsMetadata.ts index 299b64ad..94f36aaf 100644 --- a/src/ipfs/IpfsMetadata.ts +++ b/src/ipfs/IpfsMetadata.ts @@ -18,8 +18,6 @@ import { import IpfsAdapter from './IpfsAdapter'; import CloudflareReadonlyAdapter from './CloudflareReadonlyAdapter'; -export { MetadataType } from '@colony/colony-event-metadata-parser'; - const fetchRetry = wrapFetch(fetch, { headers: { Accept: 'application/json', diff --git a/src/ipfs/index.ts b/src/ipfs/index.ts index c3866824..82a9f1a8 100644 --- a/src/ipfs/index.ts +++ b/src/ipfs/index.ts @@ -1,9 +1,4 @@ -export { - IpfsMetadata, - MetadataEvent, - MetadataType, - MetadataValue, -} from './IpfsMetadata'; +export { IpfsMetadata, MetadataEvent, MetadataValue } from './IpfsMetadata'; export { default as PinataAdapter } from './PinataAdapter'; export { default as CloudflareReadonlyAdapter } from './CloudflareReadonlyAdapter'; diff --git a/src/utils.ts b/src/utils.ts index 7bd8891d..e1e502a8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,6 +10,7 @@ import { } from './types'; /** Extract event args from a contract receipt */ +// TODO: Make it possible to retrieve multiple events of the same kind export const extractEvent = ( eventName: string, receipt: ContractReceipt | ParsedLogTransactionReceipt,