Skip to content

Commit

Permalink
Feature: Add daoUpdateAction encoders and decoders (#289)
Browse files Browse the repository at this point in the history
  • Loading branch information
josemarinas authored Oct 18, 2023
1 parent 4a927a2 commit bf3e275
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 6 deletions.
5 changes: 5 additions & 0 deletions modules/client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ TEMPLATE:
## [UPCOMING]
### Added

- Helper for `daoUpdateAction` in `Client`

## [1.15.0]
### Added

- Add input validation on client functions
- Pagination on getMembers function
- Support for sepolia
Expand Down
70 changes: 70 additions & 0 deletions modules/client/examples/05-encoders-decoders/16-dao-update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* MARKDOWN
---
title: Update a DAO
---
## Generate a DAO Update action
Given a DAO address, the previous version and the new DAO Factory address, it generates the actions to update a DAO.
### Encoding
*/

import {
Client,
DaoUpdateDecodedParams,
DaoUpdateParams,
} from "@aragon/sdk-client";
import {
DaoAction,
LIVE_CONTRACTS,
} from "@aragon/sdk-client-common";
import { context } from "../index";

// Instantiates an Aragon OSx SDK client.
const client: Client = new Client(context);

const daoUpdateParams: DaoUpdateParams = {
previousVersion: [1, 0, 0],
daoFactoryAddress: LIVE_CONTRACTS["1.3.0"].base.daoFactoryAddress, // if not specified it will use the latest version in the context network
initData: new Uint8Array([12, 34, 45, 56]), // data needed to update specified, empty by default
};

const daoAddressOrEns: string = "0x123123123123123123123123123123123123"; // "my-dao.eth"

const actions: DaoAction = await client.encoding.daoUpdateAction(
daoAddressOrEns,
daoUpdateParams,
);
console.log(actions);

/* MARKDOWN
```json
{
to: "0x123123123...",
value: 0n,
data: Uint8Array[12,34,45...]
},
```
### Decoding
*/

// Decodes the apply update action for a Multisig plugin.
const decodedParams: DaoUpdateDecodedParams = client.decoding
.daoUpdateAction(actions[1].data);
console.log({ decodedParams });

/* MARKDOWN
Returns:
```json
{ decodedParams:
{
previousVersion: [1, 0, 0],
implementationAddress: "0x123123123...", // DAO base implementation address
initData: Uint8Array[12,34,45...]
}
}
```
*/
23 changes: 23 additions & 0 deletions modules/client/src/internal/client/decoding.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
DaoMetadata,
DaoUpdateDecodedParams,
GrantPermissionDecodedParams,
GrantPermissionWithConditionParams,
InitializeFromParams,
Expand Down Expand Up @@ -352,4 +353,26 @@ export class ClientDecoding extends ClientCore implements IClientDecoding {
Uint8ArraySchema.strict().validate(data);
return findInterface(data, AVAILABLE_FUNCTION_SIGNATURES);
}

/**
* Decodes the dao update params from a daoUpdateAction
*
* @param {Uint8Array} data
* @return {*} {DaoUpdateDecodedParams}
* @memberof ClientDecoding
*/
public daoUpdateAction(
data: Uint8Array,
): DaoUpdateDecodedParams {
const upgradeToAndCallDecodedParams = this.upgradeToAndCallAction(data);
const initializeFromDecodedParams = this.initializeFromAction(
upgradeToAndCallDecodedParams.data,
);
return {
implementationAddress:
upgradeToAndCallDecodedParams.implementationAddress,
previousVersion: initializeFromDecodedParams.previousVersion,
initData: initializeFromDecodedParams.initData,
};
}
}
33 changes: 33 additions & 0 deletions modules/client/src/internal/client/encoding.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
DaoUpdateParams,
GrantPermissionParams,
GrantPermissionWithConditionParams,
InitializeFromParams,
Expand All @@ -10,6 +11,7 @@ import {
import { isAddress } from "@ethersproject/address";
import {
DAO__factory,
DAOFactory__factory,
PluginSetupProcessor__factory,
} from "@aragon/osx-ethers";
import {
Expand Down Expand Up @@ -45,6 +47,7 @@ import {
} from "@aragon/sdk-client-common";
import { Interface } from "@ethersproject/abi";
import {
DaoUpdateSchema,
InitializeFromSchema,
PermissionBaseSchema,
PermissionWithConditionSchema,
Expand Down Expand Up @@ -575,4 +578,34 @@ export class ClientEncoding extends ClientCore implements IClientEncoding {
data: hexToBytes(hexBytes),
};
}

/**
* Does the necessary steps to encode an action that updates a DAO
*
* @param {string} daoAddressOrEns
* @param {DaoUpdateParams} params
* @return {*}
* @memberof ClientEncoding
*/
public async daoUpdateAction(
daoAddressOrEns: string,
params: DaoUpdateParams,
): Promise<DaoAction> {
AddressOrEnsSchema.strict().validateSync(daoAddressOrEns);
DaoUpdateSchema.strict().validateSync(params);
const initializeFromAction = this.initializeFromAction(
daoAddressOrEns,
params,
);
const { daoFactoryAddress } = params;
const daoFactory = DAOFactory__factory.connect(
daoFactoryAddress || this.web3.getAddress("daoFactoryAddress"),
this.web3.getProvider(),
);
const implementation = await daoFactory.daoBase();
return this.upgradeToAndCallAction(daoAddressOrEns, {
implementationAddress: implementation,
data: initializeFromAction.data,
});
}
}
11 changes: 10 additions & 1 deletion modules/client/src/internal/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
DaoListItem,
DaoMetadata,
DaoQueryParams,
DaoUpdateDecodedParams,
DaoUpdateParams,
DaoUpdateProposalValidity,
DecodedApplyUninstallationParams,
DepositParams,
Expand Down Expand Up @@ -100,7 +102,7 @@ export interface IClientMethods {
isPluginUpdateProposal: (
actions: DaoAction[],
) => boolean;

isDaoUpdateProposal: (
actions: DaoAction[],
) => boolean;
Expand Down Expand Up @@ -170,6 +172,10 @@ export interface IClientEncoding {
daoAddressOrEns: string,
params: InitializeFromParams,
) => DaoAction;
daoUpdateAction: (
daoAddressOrEns: string,
params: DaoUpdateParams,
) => Promise<DaoAction>;
}

export interface IClientDecoding {
Expand Down Expand Up @@ -203,6 +209,9 @@ export interface IClientDecoding {
initializeFromAction: (
data: Uint8Array,
) => InitializeFromParams;
daoUpdateAction: (
data: Uint8Array,
) => DaoUpdateDecodedParams;
}

export interface IClientEstimation {
Expand Down
6 changes: 6 additions & 0 deletions modules/client/src/internal/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,9 @@ export const InitializeFromSchema = object({
previousVersion: array().of(number()).length(3).required(),
initData: Uint8ArraySchema.notRequired(),
});

export const DaoUpdateSchema = object({
previousVersion: array().of(number()).length(3).required(),
initData: Uint8ArraySchema.notRequired(),
daoFactoryAddress: AddressOrEnsSchema.notRequired(),
});
10 changes: 9 additions & 1 deletion modules/client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export type PluginRepoCurrent = {
type PluginRepoBase = {
address: string;
subdomain: string;
current: PluginRepoCurrent
current: PluginRepoCurrent;
releases: PluginRepoRelease[];
};

Expand Down Expand Up @@ -459,3 +459,11 @@ export type IsDaoUpdateProposalValidParams = {
proposalId: string;
version?: [number, number, number];
};

export type DaoUpdateParams = InitializeFromParams & {
daoFactoryAddress?: string;
};

export type DaoUpdateDecodedParams = InitializeFromParams & {
implementationAddress: string;
};
44 changes: 43 additions & 1 deletion modules/client/test/integration/client/decoding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ declare const describe, it, expect;

// mocks need to be at the top of the imports
import { mockedIPFSClient } from "../../mocks/aragon-sdk-ipfs";
import * as ganacheSetup from "../../helpers/ganache-setup";
import * as deployContracts from "../../helpers/deployContracts";

import {
Client,
DaoMetadata,
DaoUpdateParams,
GrantPermissionDecodedParams,
GrantPermissionParams,
GrantPermissionWithConditionParams,
Expand Down Expand Up @@ -34,16 +37,28 @@ import {
bytesToHex,
Context,
hexToBytes,
LIVE_CONTRACTS,
PermissionIds,
PermissionOperationType,
Permissions,
SupportedVersion,
TokenType,
} from "@aragon/sdk-client-common";
import { Server } from "ganache";

describe("Client", () => {
beforeAll(() => {
let deployment: deployContracts.Deployment;
let server: Server;
beforeAll(async () => {
server = await ganacheSetup.start();
deployment = await deployContracts.deploy();
contextParamsLocalChain.ensRegistryAddress = ADDRESS_ONE;
contextParamsLocalChain.pluginSetupProcessorAddress = ADDRESS_TWO;
LIVE_CONTRACTS[SupportedVersion.LATEST].local.daoFactoryAddress =
deployment.daoFactory.address;
});
afterAll(async () => {
await server.close();
});
describe("Action decoders", () => {
it("Should decode an encoded grant action", () => {
Expand Down Expand Up @@ -815,5 +830,32 @@ describe("Client", () => {
bytesToHex(decodedParams.initData as Uint8Array),
);
});
it("Should decode an dao update action", async () => {
const context = new Context(contextParamsLocalChain);
const client = new Client(context);
const daoFactoryAddress = LIVE_CONTRACTS["1.3.0"].local.daoFactoryAddress;
const params: DaoUpdateParams = {
previousVersion: [1, 0, 0],
daoFactoryAddress,
initData: new Uint8Array([0, 1, 2, 3]),
};
const action = await client.encoding.daoUpdateAction(
"0x1234567890123456789012345678901234567890",
params,
);
const implementationAddress = await client.methods.getDaoImplementation(
daoFactoryAddress,
);
const decodedParams = client.decoding.daoUpdateAction(action.data);
expect(bytesToHex(params.initData as Uint8Array)).toBe(
bytesToHex(decodedParams.initData as Uint8Array),
);
expect(implementationAddress).toBe(
decodedParams.implementationAddress,
);
expect(params.previousVersion.toString()).toBe(
decodedParams.previousVersion.toString(),
);
});
});
});
Loading

0 comments on commit bf3e275

Please sign in to comment.