Skip to content

Commit

Permalink
[ADHOC] feat(sdk): add OpenAllowList as zero config extension of Simp…
Browse files Browse the repository at this point in the history
…leDenyList (#112)
  • Loading branch information
sammccord authored Sep 26, 2024
2 parents fc4b361 + b811d0b commit a9be1c2
Show file tree
Hide file tree
Showing 17 changed files with 147 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-eagles-collect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@boostxyz/sdk": minor
---

add `OpenAllowList`, for zero config allow list support
2 changes: 1 addition & 1 deletion packages/sdk/src/Actions/Action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function basicErc721TransferAction(

export function cloneEventAction(fixtures: Fixtures, erc721: MockERC721) {
return function cloneEventAction() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.EventAction(basicErc721TransferAction(erc721)),
);
Expand Down
6 changes: 3 additions & 3 deletions packages/sdk/src/Actions/ContractAction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const mintSelector = toFunctionSelector(

function payableContractAction(fixtures: Fixtures, erc20: MockERC20) {
return function payableContractAction() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.ContractAction({
chainId: BigInt(31_337),
Expand All @@ -50,7 +50,7 @@ function payableContractAction(fixtures: Fixtures, erc20: MockERC20) {

function nonPayableAction(fixtures: Fixtures, erc20: MockERC20) {
return function nonPayableAction() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.ContractAction({
chainId: BigInt(31_337),
Expand All @@ -64,7 +64,7 @@ function nonPayableAction(fixtures: Fixtures, erc20: MockERC20) {

function otherAction(fixtures: Fixtures, erc20: MockERC20) {
return function nonPayableAction() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.ContractAction({
chainId: BigInt(31_337) + 1n,
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/Actions/ERC721MintAction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const mintSelector = toFunctionSelector('function mint(address to)');

function nonPayableAction(fixtures: Fixtures, erc721: MockERC721) {
return function nonPayableAction() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.ERC721MintAction({
chainId: BigInt(31_337),
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/Actions/EventAction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function basicErc721MintFuncAction(

function cloneEventAction(fixtures: Fixtures, erc721: MockERC721) {
return function cloneEventAction() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.EventAction(basicErc721TransferAction(erc721)),
);
Expand Down
4 changes: 2 additions & 2 deletions packages/sdk/src/AllowLists/AllowList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ beforeAll(async () => {

function freshAllowList(fixtures: Fixtures) {
return function freshAllowList() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.SimpleAllowList({
owner: defaultOptions.account.address,
Expand All @@ -31,7 +31,7 @@ function freshAllowList(fixtures: Fixtures) {

function freshDenyList(fixtures: Fixtures) {
return function freshDenyList() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.SimpleDenyList({
owner: defaultOptions.account.address,
Expand Down
8 changes: 5 additions & 3 deletions packages/sdk/src/AllowLists/AllowList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ import { readContract } from '@wagmi/core';
import type { Address, Hex } from 'viem';
import type { DeployableOptions } from '../Deployable/Deployable';
import { InvalidComponentInterfaceError } from '../errors';
import { OpenAllowList } from './OpenAllowList';
import { SimpleAllowList } from './SimpleAllowList';
import { SimpleDenyList } from './SimpleDenyList';

export { SimpleAllowList, SimpleDenyList };
export { OpenAllowList, SimpleAllowList, SimpleDenyList };

/**
* A union type representing all valid protocol AllowList implementations
*
* @export
* @typedef {AllowList}
*/
export type AllowList = SimpleAllowList | SimpleDenyList;
export type AllowList = OpenAllowList | SimpleAllowList | SimpleDenyList;

/**
* A map of AllowList component interfaces to their constructors.
Expand All @@ -32,6 +33,7 @@ export const AllowListByComponentInterface = {

/**
* A function that will read a contract's component interface using `getComponentInterface` and return the correct instantiated instance.
* This function will never return an instance of {@link OpenAllowList} because it has the same component interface as {@link SimpleDenyList}
*
* @export
* @async
Expand All @@ -56,5 +58,5 @@ export async function allowListFromAddress(
interfaceId as Hex,
);
}
return new Ctor(options, address);
return new Ctor(options, address) as SimpleDenyList | SimpleAllowList;
}
40 changes: 40 additions & 0 deletions packages/sdk/src/AllowLists/OpenAllowList.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { isAddress, zeroAddress } from 'viem';
import { beforeAll, describe, expect, test } from 'vitest';
import { accounts } from '../../test/accounts';
import {
type Fixtures,
defaultOptions,
deployFixtures,
} from '../../test/helpers';
import { OpenAllowList } from './OpenAllowList';

let fixtures: Fixtures;

beforeAll(async () => {
fixtures = await loadFixture(deployFixtures);
});

function freshOpenAllowList(fixtures: Fixtures) {
return function freshOpenAllowList() {
return fixtures.registry.initialize(
crypto.randomUUID(),
new fixtures.bases.OpenAllowList(defaultOptions),
);
};
}

describe('OpenAllowList', () => {
test('can successfully be deployed', async () => {
const denyList = new OpenAllowList(defaultOptions);
await denyList.deploy();
expect(isAddress(denyList.assertValidAddress())).toBe(true);
});

test('allows anyone', async () => {
const denyList = await loadFixture(freshOpenAllowList(fixtures));
expect(await denyList.isAllowed(defaultOptions.account.address)).toBe(true);
expect(await denyList.isAllowed(zeroAddress)).toBe(true);
expect(await denyList.isAllowed(accounts.at(1)!.account)).toBe(true);
});
});
45 changes: 45 additions & 0 deletions packages/sdk/src/AllowLists/OpenAllowList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { simpleDenyListAbi } from '@boostxyz/evm';
import { bytecode } from '@boostxyz/evm/artifacts/contracts/allowlists/SimpleDenyList.sol/SimpleDenyList.json';
import { type Hex, zeroAddress } from 'viem';
import type {
DeployableOptions,
GenericDeployableParams,
} from '../Deployable/Deployable';
import {
SimpleDenyList,
type SimpleDenyListPayload,
prepareSimpleDenyListPayload,
} from './SimpleDenyList';

export const openAllowListAbi = simpleDenyListAbi;

/**
* A simple AllowList, extending {@link DenyList}, that is ownerless and allows anyone to claim.
*
* @export
* @class OpenAllowList
* @typedef {OpenAllowList}
* @extends {DeployableTarget<OpenAllowListPayload>}
*/
export class OpenAllowList extends SimpleDenyList<undefined> {
/**
* @inheritdoc
*
* @public
* @param {?SimpleDenyListPayload} [_payload]
* @param {?DeployableOptions} [_options]
* @returns {GenericDeployableParams}
*/
public override buildParameters(
_payload?: SimpleDenyListPayload,
_options?: DeployableOptions,
): GenericDeployableParams {
const [_, options] = this.validateDeploymentConfig({}, _options);
return {
abi: openAllowListAbi,
bytecode: bytecode as Hex,
args: [prepareSimpleDenyListPayload({ owner: zeroAddress, denied: [] })],
...this.optionallyAttachAccount(options.account),
};
}
}
2 changes: 1 addition & 1 deletion packages/sdk/src/AllowLists/SimpleAllowList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ beforeAll(async () => {

function freshAllowList(fixtures: Fixtures) {
return function freshAllowList() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.SimpleAllowList({
owner: defaultOptions.account.address,
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/AllowLists/SimpleDenyList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ beforeAll(async () => {

function freshDenyList(fixtures: Fixtures) {
return function freshDenyList() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.SimpleDenyList({
owner: defaultOptions.account.address,
Expand Down
15 changes: 6 additions & 9 deletions packages/sdk/src/AllowLists/SimpleDenyList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ export type SimpleDenyListLog<
* @typedef {SimpleDenyList}
* @extends {DeployableTarget<SimpleDenyListPayload>}
*/
export class SimpleDenyList extends DeployableTarget<
SimpleDenyListPayload,
typeof simpleDenyListAbi
> {
export class SimpleDenyList<
Payload = SimpleDenyListPayload,
> extends DeployableTarget<Payload | undefined, typeof simpleDenyListAbi> {
public override readonly abi = simpleDenyListAbi;
/**
* @inheritdoc
Expand Down Expand Up @@ -177,13 +176,11 @@ export class SimpleDenyList extends DeployableTarget<
* @returns {GenericDeployableParams}
*/
public override buildParameters(
_payload?: SimpleDenyListPayload,
_payload?: Payload,
_options?: DeployableOptions,
): GenericDeployableParams {
const [payload, options] = this.validateDeploymentConfig(
_payload,
_options,
);
const [p, options] = this.validateDeploymentConfig(_payload, _options);
const payload = p as SimpleDenyListPayload;
if (!payload.owner || payload.owner === zeroAddress) {
const owner = options.account
? options.account.address
Expand Down
19 changes: 19 additions & 0 deletions packages/sdk/src/BoostCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import { type Action, actionFromAddress } from './Actions/Action';
import { EventAction, type EventActionPayload } from './Actions/EventAction';
import { type AllowList, allowListFromAddress } from './AllowLists/AllowList';
import { OpenAllowList } from './AllowLists/OpenAllowList';
import {
SimpleAllowList,
type SimpleAllowListPayload,
Expand Down Expand Up @@ -1008,6 +1009,24 @@ export class BoostCore extends Deployable<
// isBase,
// );
// }
/**
* Bound {@link OpenAllowList} constructor that reuses the same configuration as the Boost Core instance.
*
* @example
* ```ts
* const list = core.OpenAllowList('0x') // is roughly equivalent to
* const list = new OpenAllowList({ config: core._config, account: core._account }, '0x')
* ```
* @param {?boolean} [isBase]
* @returns {OpenAllowList}
*/
OpenAllowList(isBase?: boolean) {
return new OpenAllowList(
{ config: this._config, account: this._account },
undefined,
isBase,
);
}
/**
* Bound {@link SimpleAllowList} constructor that reuses the same configuration as the Boost Core instance.
*
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/Deployable/DeployableTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class DeployableTarget<
*/
constructor(
options: DeployableOptions,
payload: DeployablePayloadOrAddress<Payload>,
payload?: DeployablePayloadOrAddress<Payload>,
isBase?: boolean,
) {
super(options, payload);
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/Incentives/AllowListIncentive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let fixtures: Fixtures;

function freshAllowList(fixtures: Fixtures) {
return function freshAllowList() {
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.SimpleAllowList({
owner: defaultOptions.account.address,
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/Validators/SignerValidator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function freshValidator(fixtures: Fixtures) {
return function freshValidator() {
// biome-ignore lint/style/noNonNullAssertion: this will never be undefined
const account = accounts.at(1)!.account;
return fixtures.registry.clone(
return fixtures.registry.initialize(
crypto.randomUUID(),
fixtures.core.SignerValidator({
signers: [defaultOptions.account.address, account],
Expand Down
17 changes: 14 additions & 3 deletions packages/sdk/test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
type EventActionPayload,
FilterType,
ManagedBudget,
OpenAllowList,
PointsIncentive,
type PointsIncentivePayload,
PrimitiveType,
Expand Down Expand Up @@ -299,6 +300,9 @@ export async function deployFixtures(
SimpleDenyList: class TSimpleDenyList extends SimpleDenyList {
public static override base = simpleDenyListBase;
},
OpenAllowList: class TOpenAllowList extends OpenAllowList {
public static override base = simpleDenyListBase;
},
SimpleBudget: class TSimpleBudget extends SimpleBudget {
public static override base = simpleBudgetBase;
},
Expand Down Expand Up @@ -386,6 +390,13 @@ export async function deployFixtures(
isBase,
);
}
override OpenAllowList(isBase?: boolean) {
return new bases.OpenAllowList(
{ config: this._config, account: this._account },
undefined,
isBase,
);
}
SimpleBudget(options: DeployablePayloadOrAddress<SimpleBudgetPayload>) {
return new bases.SimpleBudget(
{ config: this._config, account: this._account },
Expand Down Expand Up @@ -477,7 +488,7 @@ export function freshBudget(
fixtures: Fixtures,
) {
return async function freshBudget() {
return await fixtures.registry.clone(
return await fixtures.registry.initialize(
crypto.randomUUID(),
new fixtures.bases.SimpleBudget(options, {
owner: options.account.address,
Expand All @@ -495,7 +506,7 @@ export function freshManagedBudget(
fixtures: Fixtures,
) {
return async function freshBudget() {
return await fixtures.registry.clone(
return await fixtures.registry.initialize(
crypto.randomUUID(),
new fixtures.bases.ManagedBudget(options, {
owner: options.account.address,
Expand All @@ -514,7 +525,7 @@ export function freshVestingBudget(
fixtures: Fixtures,
) {
return async function freshVestingBudget() {
return await fixtures.registry.clone(
return await fixtures.registry.initialize(
crypto.randomUUID(),
new fixtures.bases.VestingBudget(options, {
owner: options.account.address,
Expand Down

0 comments on commit a9be1c2

Please sign in to comment.