diff --git a/package.json b/package.json index 6a9f4006..75adf938 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,7 @@ "type": "module", "repository": "https://github.com/rabbitholegg/boost-protocol", "author": "Boost Team", - "workspaces": [ - "packages/*" - ], + "workspaces": ["packages/*"], "packageManager": "pnpm@8.15.8", "engines": { "pnpm": "8.15.8" diff --git a/packages/evm/package.json b/packages/evm/package.json index 9a96277d..6a556471 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -3,11 +3,7 @@ "version": "0.0.0-alpha.0", "description": "", "private": true, - "files": [ - "dist", - "artifacts", - "contracts" - ], + "files": ["dist", "artifacts", "contracts"], "type": "module", "repository": "https://github.com/rabbitholegg/boost-protocol", "author": "Boost Team", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index f85e6b42..60503ed3 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -10,8 +10,9 @@ ".": "./src/index.ts", "./errors": "./src/errors.ts", "./Boost": "./src/Boost.ts", - "./BoostClient": "./src/BoostClient.ts", + "./BoostCore": "./src/BoostCore.ts", "./Deployable/Deployable": "./src/Deployable/Deployable.ts", + "./Deployable/DeployableTarget": "./src/Deployable/DeployableTarget.ts", "./Deployable/Contract": "./src/Deployable/Contract.ts", "./Actions/Action": "./src/Actions/Action.ts", "./Actions/ContractAction": "./src/Actions/ContractAction.ts", @@ -51,11 +52,11 @@ "node": "./dist/Boost.js", "types": "./dist/Boost.d.ts" }, - "./BoostClient": { - "require": "./dist/BoostClient.cjs", - "import": "./dist/BoostClient.js", - "node": "./dist/BoostClient.js", - "types": "./dist/BoostClient.d.ts" + "./BoostCore": { + "require": "./dist/BoostCore.cjs", + "import": "./dist/BoostCore.js", + "node": "./dist/BoostCore.js", + "types": "./dist/BoostCore.d.ts" }, "./Deployable/Deployable": { "require": "./dist/Deployable/Deployable.cjs", @@ -63,6 +64,12 @@ "node": "./dist/Deployable/Deployable.js", "types": "./dist/Deployable/Deployable.d.ts" }, + "./Deployable/DeployableTarget": { + "require": "./dist/Deployable/DeployableTarget.cjs", + "import": "./dist/Deployable/DeployableTarget.js", + "node": "./dist/Deployable/DeployableTarget.js", + "types": "./dist/Deployable/DeployableTarget.d.ts" + }, "./Deployable/Contract": { "require": "./dist/Deployable/Contract.cjs", "import": "./dist/Deployable/Contract.js", diff --git a/packages/sdk/src/Actions/ContractAction.ts b/packages/sdk/src/Actions/ContractAction.ts index 9e1ac1e7..7085d2b9 100644 --- a/packages/sdk/src/Actions/ContractAction.ts +++ b/packages/sdk/src/Actions/ContractAction.ts @@ -1,5 +1,3 @@ -import type { Config } from '@wagmi/core'; -import type { Hex } from 'viem'; import { type ContractActionPayload, prepareContractActionPayload, @@ -9,22 +7,25 @@ import { readContractActionTarget, readContractActionValue, writeContractActionExecute, -} from '../../../evm/artifacts'; -import ContractActionArtifact from '../../../evm/artifacts/contracts/actions/ContractAction.sol/ContractAction.json'; -import { - Deployable, - type GenericDeployableParams, +} from '@boostxyz/evm'; +import ContractActionArtifact from '@boostxyz/evm/artifacts/contracts/actions/ContractAction.sol/ContractAction.json'; +import type { Hex } from 'viem'; +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import type { CallParams } from '../utils'; export type { ContractActionPayload }; -export class ContractAction extends Deployable { +export class ContractAction extends DeployableTarget { public async chainId( params: CallParams = {}, ) { return readContractActionChainId(this._config, { address: this.assertValidAddress(), + ...this.optionallyAttachAccount(), ...params, }); } @@ -34,6 +35,7 @@ export class ContractAction extends Deployable { ) { return readContractActionTarget(this._config, { address: this.assertValidAddress(), + ...this.optionallyAttachAccount(), ...params, }); } @@ -43,6 +45,7 @@ export class ContractAction extends Deployable { ) { return readContractActionSelector(this._config, { address: this.assertValidAddress(), + ...this.optionallyAttachAccount(), ...params, }); } @@ -50,6 +53,7 @@ export class ContractAction extends Deployable { public async value(params: CallParams = {}) { return readContractActionValue(this._config, { address: this.assertValidAddress(), + ...this.optionallyAttachAccount(), ...params, }); } @@ -61,6 +65,7 @@ export class ContractAction extends Deployable { return writeContractActionExecute(this._config, { address: this.assertValidAddress(), args: [data], + ...this.optionallyAttachAccount(), ...params, }); } @@ -72,19 +77,24 @@ export class ContractAction extends Deployable { return readContractActionPrepare(this._config, { address: this.assertValidAddress(), args: [data], + ...this.optionallyAttachAccount(), ...params, }); } public override buildParameters( _payload?: ContractActionPayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); return { abi: ContractActionArtifact.abi, bytecode: ContractActionArtifact.bytecode as Hex, args: [prepareContractActionPayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Actions/ERC721MintAction.ts b/packages/sdk/src/Actions/ERC721MintAction.ts index 1b6b5e34..1be39e81 100644 --- a/packages/sdk/src/Actions/ERC721MintAction.ts +++ b/packages/sdk/src/Actions/ERC721MintAction.ts @@ -1,15 +1,16 @@ -import type { Config } from '@wagmi/core'; -import type { Hex } from 'viem'; import { type ERC721MintActionPayload, prepareERC721MintActionPayload, readErc721MintActionPrepare, writeErc721MintActionExecute, writeErc721MintActionValidate, -} from '../../../evm/artifacts'; -import ERC721MintActionArtifact from '../../../evm/artifacts/contracts/actions/ERC721MintAction.sol/ERC721MintAction.json'; +} from '@boostxyz/evm'; +import ERC721MintActionArtifact from '@boostxyz/evm/artifacts/contracts/actions/ERC721MintAction.sol/ERC721MintAction.json'; +import type { Config } from '@wagmi/core'; +import type { Hex } from 'viem'; import { Deployable, + type DeployableOptions, type GenericDeployableParams, } from '../Deployable/Deployable'; import type { CallParams } from '../utils'; @@ -25,6 +26,7 @@ export class ERC721MintAction extends ContractAction { return writeErc721MintActionExecute(this._config, { address: this.assertValidAddress(), args: [data], + ...this.optionallyAttachAccount(), ...params, }); } @@ -36,6 +38,7 @@ export class ERC721MintAction extends ContractAction { return readErc721MintActionPrepare(this._config, { address: this.assertValidAddress(), args: [data], + ...this.optionallyAttachAccount(), ...params, }); } @@ -47,19 +50,24 @@ export class ERC721MintAction extends ContractAction { return writeErc721MintActionValidate(this._config, { address: this.assertValidAddress(), args: [data], + ...this.optionallyAttachAccount(), ...params, }); } public override buildParameters( _payload?: ERC721MintActionPayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); return { abi: ERC721MintActionArtifact.abi, bytecode: ERC721MintActionArtifact.bytecode as Hex, args: [prepareERC721MintActionPayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/AllowLists/SimpleAllowList.ts b/packages/sdk/src/AllowLists/SimpleAllowList.ts index d3b6b8c9..8c9d0807 100644 --- a/packages/sdk/src/AllowLists/SimpleAllowList.ts +++ b/packages/sdk/src/AllowLists/SimpleAllowList.ts @@ -1,22 +1,23 @@ -import { type Config, getAccount } from '@wagmi/core'; -import { type Address, type Hex, zeroAddress, zeroHash } from 'viem'; import { type SimpleAllowListPayload, prepareSimpleAllowListPayload, readSimpleAllowListIsAllowed, writeSimpleAllowListSetAllowed, -} from '../../../evm/artifacts'; -import SimpleAllowListArtifact from '../../../evm/artifacts/contracts/allowlists/SimpleAllowList.sol/SimpleAllowList.json'; -import { - Deployable, - type GenericDeployableParams, +} from '@boostxyz/evm'; +import SimpleAllowListArtifact from '@boostxyz/evm/artifacts/contracts/allowlists/SimpleAllowList.sol/SimpleAllowList.json'; +import { getAccount } from '@wagmi/core'; +import { type Address, type Hex, zeroAddress, zeroHash } from 'viem'; +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import { DeployableUnknownOwnerProvidedError } from '../errors'; import type { CallParams } from '../utils'; export type { SimpleAllowListPayload }; -export class SimpleAllowList extends Deployable { +export class SimpleAllowList extends DeployableTarget { public async isAllowed( address: Address, params: CallParams = {}, @@ -24,6 +25,7 @@ export class SimpleAllowList extends Deployable { return await readSimpleAllowListIsAllowed(this._config, { address: this.assertValidAddress(), args: [address, zeroHash], + ...this.optionallyAttachAccount(), ...params, }); } @@ -41,11 +43,18 @@ export class SimpleAllowList extends Deployable { } public override buildParameters( _payload?: SimpleAllowListPayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload, config] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); if (!payload.owner || payload.owner === zeroAddress) { - const owner = getAccount(config).address; + const owner = options.account + ? options.account.address + : options.config + ? getAccount(options.config).address + : this._account?.address; if (owner) { payload.owner = owner; } else { @@ -56,6 +65,7 @@ export class SimpleAllowList extends Deployable { abi: SimpleAllowListArtifact.abi, bytecode: SimpleAllowListArtifact.bytecode as Hex, args: [prepareSimpleAllowListPayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/AllowLists/SimpleDenyList.ts b/packages/sdk/src/AllowLists/SimpleDenyList.ts index 6d283c51..7ee22aa3 100644 --- a/packages/sdk/src/AllowLists/SimpleDenyList.ts +++ b/packages/sdk/src/AllowLists/SimpleDenyList.ts @@ -1,22 +1,23 @@ -import { type Config, getAccount } from '@wagmi/core'; -import { type Address, type Hex, zeroAddress, zeroHash } from 'viem'; import { type SimpleDenyListPayload, prepareSimpleDenyListPayload, readSimpleDenyListIsAllowed, writeSimpleDenyListSetDenied, -} from '../../../evm/artifacts'; -import SimpleDenyListArtifact from '../../../evm/artifacts/contracts/allowlists/SimpleDenyList.sol/SimpleDenyList.json'; -import { - Deployable, - type GenericDeployableParams, +} from '@boostxyz/evm'; +import SimpleDenyListArtifact from '@boostxyz/evm/artifacts/contracts/allowlists/SimpleDenyList.sol/SimpleDenyList.json'; +import { getAccount } from '@wagmi/core'; +import { type Address, type Hex, zeroAddress, zeroHash } from 'viem'; +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import { DeployableUnknownOwnerProvidedError } from '../errors'; import type { CallParams } from '../utils'; export type { SimpleDenyListPayload }; -export class SimpleDenyList extends Deployable { +export class SimpleDenyList extends DeployableTarget { public async isAllowed( address: Address, params: CallParams = {}, @@ -42,11 +43,18 @@ export class SimpleDenyList extends Deployable { public override buildParameters( _payload?: SimpleDenyListPayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload, config] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); if (!payload.owner || payload.owner === zeroAddress) { - const owner = getAccount(config).address; + const owner = options.account + ? options.account.address + : options.config + ? getAccount(options.config).address + : this._account?.address; if (owner) { payload.owner = owner; } else { @@ -57,6 +65,7 @@ export class SimpleDenyList extends Deployable { abi: SimpleDenyListArtifact.abi, bytecode: SimpleDenyListArtifact.bytecode as Hex, args: [prepareSimpleDenyListPayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Boost.ts b/packages/sdk/src/Boost.ts index 4659e56b..7e6cd04a 100644 --- a/packages/sdk/src/Boost.ts +++ b/packages/sdk/src/Boost.ts @@ -1,13 +1,11 @@ -import type { Config } from '@wagmi/core'; -import { type Address, zeroAddress } from 'viem'; +import { type Address, type Hex, zeroAddress } from 'viem'; import type { Action } from './Actions/Action'; import type { AllowList } from './AllowLists/AllowList'; import type { Budget } from './Budgets/Budget'; -import { Contract } from './Deployable/Contract'; import type { Incentive } from './Incentives/Incentive'; import type { Validator } from './Validators/Validator'; -export interface BoostPayload { +export type BoostPayload = { address?: Address; budget: Budget; action: Action; @@ -18,7 +16,7 @@ export interface BoostPayload { referralFee?: bigint; maxParticipants?: bigint; owner?: Address; -} +}; export class Boost { readonly action: Action; @@ -42,4 +40,11 @@ export class Boost { this.maxParticipants = payload.maxParticipants || 0n; this.owner = payload.owner || zeroAddress; } + + // public encode() {} + + public static decode(_data: Hex) { + // TODO decode hash, initialize class + // return new Boost({}); + } } diff --git a/packages/sdk/src/BoostClient.test.ts b/packages/sdk/src/BoostClient.test.ts index 5282626f..0271a3d1 100644 --- a/packages/sdk/src/BoostClient.test.ts +++ b/packages/sdk/src/BoostClient.test.ts @@ -1,8 +1,15 @@ -import { isAddress } from 'viem'; -import { beforeAll, expect, test } from 'vitest'; +import { zeroAddress, zeroHash } from 'viem'; +import { beforeAll, test } from 'vitest'; import { type Fixtures, deployFixtures } from '../test/helpers'; +import { setupConfig, testAccount } from '../test/viem'; +import { ContractAction } from './Actions/ContractAction'; +import { SimpleAllowList } from './AllowLists/SimpleAllowList'; +import { BoostCore } from './BoostCore'; +import { SimpleBudget } from './Budgets/Budget'; +import { SignerValidator } from './Validators/SignerValidator'; -let fixtures: Fixtures; +let fixtures: Fixtures, + config = setupConfig(); beforeAll(async () => { fixtures = await deployFixtures(); @@ -10,5 +17,47 @@ beforeAll(async () => { test('expect true', async () => { console.log(fixtures); - expect(isAddress(fixtures.core)).toBe(true); + const { core, bases } = fixtures; + const client = new BoostCore({ + config: config, + address: core, + account: testAccount, + }); + await client.createBoost({ + budget: client + .SimpleBudget({ + owner: testAccount.address, + authorized: [], + }) + .at(bases.SimpleBudget.base), + action: client + .ContractAction( + { + chainId: BigInt(31_337), + target: core, + selector: '0xdeadbeef', + value: 0n, + }, + true, + ) + .at(bases.ContractAction.base), + validator: client + .SignerValidator( + { + signers: [testAccount.address], + }, + true, + ) + .at(bases.SignerValidator.base), + allowList: client + .SimpleAllowList( + { + owner: testAccount.address, + allowed: [], + }, + true, + ) + .at(bases.SimpleAllowList.base), + incentives: [], + }); }); diff --git a/packages/sdk/src/BoostClient.ts b/packages/sdk/src/BoostClient.ts deleted file mode 100644 index 9497d862..00000000 --- a/packages/sdk/src/BoostClient.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { getAccount } from '@wagmi/core'; -import { createWriteContract } from '@wagmi/core/codegen'; -import { type Address, zeroAddress, zeroHash } from 'viem'; -import { - type BoostPayload as OnChainBoostPayload, - boostCoreAbi, - prepareBoostPayload, - readBoostCoreGetBoost, -} from '../../evm/artifacts'; -import { - ContractAction, - type ContractActionPayload, -} from './Actions/ContractAction'; -import { - ERC721MintAction, - type ERC721MintActionPayload, -} from './Actions/ERC721MintAction'; -import { - SimpleAllowList, - type SimpleAllowListPayload, -} from './AllowLists/SimpleAllowList'; -import { - SimpleDenyList, - type SimpleDenyListPayload, -} from './AllowLists/SimpleDenyList'; -import { Boost, type BoostPayload } from './Boost'; -import { SimpleBudget, type SimpleBudgetPayload } from './Budgets/SimpleBudget'; -import { - VestingBudget, - type VestingBudgetPayload, -} from './Budgets/VestingBudget'; -import type { Config } from './Deployable/Contract'; -import type { Deployable, DeployableOptions } from './Deployable/Deployable'; -import { - AllowListIncentive, - type AllowListIncentivePayload, -} from './Incentives/AllowListIncentive'; -import { - CGDAIncentive, - type CGDAIncentivePayload, -} from './Incentives/CGDAIncentive'; -import { - ERC20Incentive, - type ERC20IncentivePayload, -} from './Incentives/ERC20Incentive'; -import { - ERC1155Incentive, - type ERC1155IncentivePayload, -} from './Incentives/ERC1155Incentive'; -import { - PointsIncentive, - type PointsIncentivePayload, -} from './Incentives/PointsIncentive'; -import { - SignerValidator, - type SignerValidatorPayload, -} from './Validators/SignerValidator'; -import { DeployableUnknownOwnerProvidedError } from './errors'; - -export const BOOST_CORE_ADDRESS: Address = import.meta.env - .VITE_BOOST_CORE_ADDRESS; - -export interface BoostClientConfig { - address?: Address; - config: Config; -} - -// TODO RFC? -// As dependencies are deployed, the iterator yields with the deployable and the remaining number of steps. finally, it yields with the address of -export interface CreateBoostProgress { - remaining: number; - deployed: Deployable; -} - -export interface CreateBoostCompletion { - address: Address; -} - -export class BoostClient { - protected address: Address = BOOST_CORE_ADDRESS; - protected config: Config; - - constructor({ address, config }: BoostClientConfig) { - if (address) this.address = address; - this.config = config; - } - - // // TODO make this transactional? if any deployment fails what do we do with the previously deployed deployables? - // // TODO revisit this, necessary? - // public async *createBoostWithProgress({ - // budget, - // action, - // validator, - // allowList, - // incentives, - // protocolFee = 0n, - // referralFee = 0n, - // maxParticipants = 0n, - // owner = zeroAddress, - // }: Omit): AsyncGenerator< - // CreateBoostProgress | CreateBoostCompletion, - // Address - // > { - // const boostFactory = createWriteContract({ - // abi: boostCoreAbi, - // functionName: 'createBoost', - // address: this.address, - // }); - - // if (!owner) { - // owner = getAccount(this.config).address || zeroAddress; - // if (owner === zeroAddress) { - // // throw? TODO - // console.warn('No owner supplied, falling back to zeroAddress'); - // } - // } - - // // As we proceed, decrement total steps to indiciate progress to consumer - // let remainingSteps = 4 + incentives.length; - - // let budgetPayload: Pick = { - // budget: budget.address || zeroAddress, - // }; - - // if (budget.address === zeroAddress) { - // budget = await this.deploy(budget); - // budgetPayload.budget = budget.address || zeroAddress; - // // TODO validate and throw? - // } - // yield { - // remaining: --remainingSteps, - // deployed: budget, - // }; - - // let actionPayload: Pick = { - // action: { - // isBase: false, - // instance: action.address || zeroAddress, - // parameters: action.buildParameters(this.config).args.at(0) || zeroHash, - // }, - // }; - // if (actionPayload.action.instance === zeroAddress) { - // action = await this.deploy(action); - // actionPayload.action.instance = action.address || zeroAddress; - // // TODO validate and throw? - // } - // yield { - // remaining: --remainingSteps, - // deployed: action, - // }; - - // let validatorPayload: Pick = { - // validator: { - // isBase: false, - // instance: validator.address || zeroAddress, - // parameters: - // validator.buildParameters(this.config).args.at(0) || zeroHash, - // }, - // }; - // if (validatorPayload.validator.instance === zeroAddress) { - // validator = await this.deploy(validator); - // validatorPayload.validator.instance = validator.address || zeroAddress; - // // TODO validate and throw? - // } - // yield { - // remaining: --remainingSteps, - // deployed: validator, - // }; - - // let allowListPayload: Pick = { - // allowList: { - // isBase: false, - // instance: allowList.address || zeroAddress, - // parameters: - // allowList.buildParameters(this.config).args.at(0) || zeroHash, - // }, - // }; - // if (allowListPayload.allowList.instance === zeroAddress) { - // allowList = await this.deploy(allowList); - // allowListPayload.allowList.instance = allowList.address || zeroAddress; - // // TODO validate and throw? - // } - // yield { - // remaining: --remainingSteps, - // deployed: allowList, - // }; - - // let incentivesPayload: Pick = { - // incentives: incentives.map((incentive) => ({ - // isBase: false, - // instance: incentive.address || zeroAddress, - // parameters: - // incentive.buildParameters(this.config).args.at(0) || zeroHash, - // })), - // }; - // for (let i = 0; i < incentives.length; i++) { - // let incentive = incentives.at(i)!; - // const incentiveTarget = incentivesPayload.incentives.at(i)!; - - // if (incentiveTarget.instance === zeroAddress) { - // incentive = await this.deploy(incentive); - // incentiveTarget.instance = incentive.address || zeroAddress; - // // TODO validate and throw? - // } - // yield { - // remaining: --remainingSteps, - // deployed: incentive, - // }; - // } - - // const boostPayload: OnChainBoostPayload = { - // ...budgetPayload, - // ...actionPayload, - // ...validatorPayload, - // ...allowListPayload, - // ...incentivesPayload, - // protocolFee, - // referralFee, - // maxParticipants, - // owner, - // }; - - // const boost = await boostFactory(this.config, { - // args: [prepareBoostPayload(boostPayload)], - // }); - - // yield { - // address: boost, - // }; - - // return boost; - // } - - // TODO make this transactional? if any deployment fails what do we do with the previously deployed deployables? - public async createBoost({ - budget, - action, - validator, - allowList, - incentives, - protocolFee = 0n, - referralFee = 0n, - maxParticipants = 0n, - owner = zeroAddress, - }: BoostPayload): Promise { - const boostFactory = createWriteContract({ - abi: boostCoreAbi, - functionName: 'createBoost', - address: this.address, - }); - - if (!owner) { - owner = getAccount(this.config).address || zeroAddress; - if (owner === zeroAddress) { - throw new DeployableUnknownOwnerProvidedError(); - } - } - - let budgetPayload: Pick = { - budget: budget.address || zeroAddress, - }; - - if (budget.address === zeroAddress) { - await budget.deploy(); - budgetPayload.budget = budget.address; - } - - let actionPayload: Pick = { - action: { - isBase: false, - instance: action.address || zeroAddress, - parameters: - action.buildParameters(action.payload, this.config).args.at(0) || - zeroHash, - }, - }; - if (actionPayload.action.instance === zeroAddress) { - await action.deploy(); - actionPayload.action.instance = action.address || zeroAddress; - } - - let validatorPayload: Pick = { - validator: { - isBase: false, - instance: validator.address || zeroAddress, - parameters: - validator - .buildParameters(validator.payload, this.config) - .args.at(0) || zeroHash, - }, - }; - if (validatorPayload.validator.instance === zeroAddress) { - await validator.deploy(); - validatorPayload.validator.instance = validator.address || zeroAddress; - } - - let allowListPayload: Pick = { - allowList: { - isBase: false, - instance: allowList.address || zeroAddress, - parameters: allowList.buildParameters().args.at(0) || zeroHash, - }, - }; - if (allowListPayload.allowList.instance === zeroAddress) { - await allowList.deploy(); - allowListPayload.allowList.instance = allowList.address || zeroAddress; - } - - let incentivesPayload: Pick = { - incentives: incentives.map((incentive) => ({ - isBase: false, - instance: incentive.address || zeroAddress, - parameters: incentive.buildParameters().args.at(0) || zeroHash, - })), - }; - for (let i = 0; i < incentives.length; i++) { - let incentive = incentives.at(i)!; - const incentiveTarget = incentivesPayload.incentives.at(i)!; - - if (incentiveTarget.instance === zeroAddress) { - incentive.deploy(); - incentiveTarget.instance = incentive.address || zeroAddress; - } - } - - const boostPayload: OnChainBoostPayload = { - ...budgetPayload, - ...actionPayload, - ...validatorPayload, - ...allowListPayload, - ...incentivesPayload, - protocolFee, - referralFee, - maxParticipants, - owner, - }; - - const _boostAddress = await boostFactory(this.config, { - args: [prepareBoostPayload(boostPayload)], - }); - - return new Boost({ - budget, - action, - validator, - allowList, - incentives, - protocolFee, - referralFee, - maxParticipants, - owner, - }); - } - - ContractAction(options: DeployableOptions) { - return new ContractAction(this.config, options); - } - ERC721MintAction(options: DeployableOptions) { - return new ERC721MintAction(this.config, options); - } - SimpleAllowList(options: DeployableOptions) { - return new SimpleAllowList(this.config, options); - } - SimpleDenyList(options: DeployableOptions) { - return new SimpleDenyList(this.config, options); - } - SimpleBudget(options: DeployableOptions) { - return new SimpleBudget(this.config, options); - } - VestingBudget(options: DeployableOptions) { - return new VestingBudget(this.config, options); - } - AllowListIncentive(options: DeployableOptions) { - return new AllowListIncentive(this.config, options); - } - CGDAIncentive(options: DeployableOptions) { - return new CGDAIncentive(this.config, options); - } - ERC20Incentive(options: DeployableOptions) { - return new ERC20Incentive(this.config, options); - } - ERC1155Incentive(options: DeployableOptions) { - return new ERC1155Incentive(this.config, options); - } - PointsIncentive(options: DeployableOptions) { - return new PointsIncentive(this.config, options); - } - SignerValidator(options: DeployableOptions) { - return new SignerValidator(this.config, options); - } -} diff --git a/packages/sdk/src/BoostCore.ts b/packages/sdk/src/BoostCore.ts new file mode 100644 index 00000000..5e9c4aa9 --- /dev/null +++ b/packages/sdk/src/BoostCore.ts @@ -0,0 +1,543 @@ +import BoostCoreArtifact from '@boostxyz/evm/artifacts/contracts/BoostCore.sol/BoostCore.json'; +import { getAccount, waitForTransactionReceipt } from '@wagmi/core'; +import { createWriteContract } from '@wagmi/core/codegen'; +import { + type Address, + type Hash, + type Hex, + decodeAbiParameters, + zeroAddress, + zeroHash, +} from 'viem'; +import { + type BoostPayload as OnChainBoostPayload, + type Target, + boostCoreAbi, + prepareBoostPayload, +} from '../../evm/artifacts'; + +import type { Action } from './Actions/Action'; +import { + ContractAction, + type ContractActionPayload, +} from './Actions/ContractAction'; +import { + ERC721MintAction, + type ERC721MintActionPayload, +} from './Actions/ERC721MintAction'; +import type { AllowList } from './AllowLists/AllowList'; +import { + SimpleAllowList, + type SimpleAllowListPayload, +} from './AllowLists/SimpleAllowList'; +import { + SimpleDenyList, + type SimpleDenyListPayload, +} from './AllowLists/SimpleDenyList'; +import type { Budget } from './Budgets/Budget'; +import { SimpleBudget, type SimpleBudgetPayload } from './Budgets/SimpleBudget'; +import { + VestingBudget, + type VestingBudgetPayload, +} from './Budgets/VestingBudget'; +import { + Deployable, + type DeployableOptions, + type DeployablePayloadOrAddress, + type GenericDeployableParams, +} from './Deployable/Deployable'; +import { + AllowListIncentive, + type AllowListIncentivePayload, +} from './Incentives/AllowListIncentive'; +import { + CGDAIncentive, + type CGDAIncentivePayload, +} from './Incentives/CGDAIncentive'; +import { + ERC20Incentive, + type ERC20IncentivePayload, +} from './Incentives/ERC20Incentive'; +import { + ERC1155Incentive, + type ERC1155IncentivePayload, +} from './Incentives/ERC1155Incentive'; +import type { Incentive } from './Incentives/Incentive'; +import { + PointsIncentive, + type PointsIncentivePayload, +} from './Incentives/PointsIncentive'; +import { + SignerValidator, + type SignerValidatorPayload, +} from './Validators/SignerValidator'; +import type { Validator } from './Validators/Validator'; +import { DeployableUnknownOwnerProvidedError } from './errors'; + +export const BOOST_CORE_ADDRESS: Address = import.meta.env + .VITE_BOOST_CORE_ADDRESS; + +export interface BoostCoreDeployedOptions extends DeployableOptions { + address?: Address; +} + +// biome-ignore lint/suspicious/noExplicitAny: type guard +function isBoostCoreDeployed(opts: any): opts is BoostCoreDeployedOptions { + return opts.address; +} + +export interface BoostCoreOptionsWithPayload extends DeployableOptions { + registryAddress: Address; + protocolFeeReceiver: Address; +} + +// biome-ignore lint/suspicious/noExplicitAny: type guard +function isBoostCoreDeployable(opts: any): opts is BoostCoreOptionsWithPayload { + return opts.registryAddress && opts.protocolFeeReceiver; +} + +export type BoostClientConfig = + | BoostCoreDeployedOptions + | BoostCoreOptionsWithPayload; + +export type CreatBoostPayload = { + budget: Budget | Address; + action: Action | Target; + validator: Validator | Target; + allowList: AllowList | Target; + incentives: Array; + protocolFee?: bigint; + referralFee?: bigint; + maxParticipants?: bigint; + owner?: Address; +}; + +// biome-ignore lint/suspicious/noExplicitAny: is a typeguard for generic target +export function isTarget(t: any): t is Target { + return ( + t?.instance !== undefined && + t?.instance !== undefined && + t?.parameters !== undefined + ); +} + +export class BoostCore extends Deployable<[Address, Address]> { + constructor({ config, account, ...options }: BoostClientConfig) { + if (isBoostCoreDeployed(options) && options.address) { + super({ account, config }, options.address); + } else if (isBoostCoreDeployable(options)) { + super({ account, config }, [ + options.registryAddress, + options.protocolFeeReceiver, + ]); + } else { + super({ account, config }, BOOST_CORE_ADDRESS); + } + } + + // TODO make this transactional? if any deployment fails what do we do with the previously deployed deployables? + public async createBoost( + _boostPayload: CreatBoostPayload, + _options: DeployableOptions = { + config: this._config, + account: this._account, + }, + ) { + const [payload, options] = this.validateDeploymentConfig( + _boostPayload, + _options, + ); + + let { + budget, + action, + validator, + allowList, + incentives, + protocolFee = 0n, + referralFee = 0n, + maxParticipants = 0n, + owner = zeroAddress, + } = payload; + + const boostFactory = createWriteContract({ + abi: boostCoreAbi, + functionName: 'createBoost', + address: this.address, + }); + + if (!owner) { + owner = + this._account?.address || + getAccount(options.config).address || + zeroAddress; + if (owner === zeroAddress) { + throw new DeployableUnknownOwnerProvidedError(); + } + } + + let budgetPayload: OnChainBoostPayload['budget'] = zeroAddress, + budgetHash: Hash | undefined = undefined; + if (typeof budget === 'string') budgetPayload = budget; + else { + if (budget.address) budgetPayload = budget.address; + else { + budgetHash = await budget.deploy(undefined, options); + } + } + + console.log(JSON.stringify({ budgetPayload })); + + let actionPayload: OnChainBoostPayload['action'] = { + instance: zeroAddress, + isBase: false, + parameters: zeroHash, + }, + actionHash: Hash | undefined = undefined; + if (isTarget(action)) actionPayload = action; + else { + if (action.address) + actionPayload = { + isBase: action.isBase, + instance: action.address, + parameters: action.isBase + ? action.buildParameters(undefined, options).args.at(0) || zeroHash + : zeroHash, + }; + else { + actionHash = await action.deploy(undefined, options); + } + } + + console.log(JSON.stringify({ actionPayload })); + + let validatorPayload: OnChainBoostPayload['validator'] = { + instance: zeroAddress, + isBase: false, + parameters: zeroHash, + }, + validatorHash: Hash | undefined = undefined; + if (isTarget(validator)) validatorPayload = validator; + else { + if (validator.address) + validatorPayload = { + isBase: validator.isBase, + instance: validator.address, + parameters: validator.isBase + ? validator.buildParameters(undefined, options).args.at(0) || + zeroHash + : zeroHash, + }; + else { + validatorHash = await validator.deploy(undefined, options); + } + } + + console.log(JSON.stringify({ validatorPayload })); + + let allowListPayload: OnChainBoostPayload['allowList'] = { + instance: zeroAddress, + isBase: false, + parameters: zeroHash, + }, + allowListHash: Hash | undefined = undefined; + if (isTarget(allowList)) allowListPayload = allowList; + else { + if (allowList.address) + allowListPayload = { + isBase: allowList.isBase, + instance: allowList.address, + parameters: allowList.isBase + ? allowList.buildParameters(undefined, options).args.at(0) || + zeroHash + : zeroHash, + }; + else { + allowListHash = await allowList.deploy(undefined, options); + } + } + + console.log(JSON.stringify({ allowListPayload })); + + let incentivesPayloads: Array = incentives.map(() => ({ + instance: zeroAddress, + isBase: false, + parameters: zeroHash, + })), + incentiveHashes = incentives.map(() => undefined); + for (let i = 0; i < incentives.length; i++) { + // biome-ignore lint/style/noNonNullAssertion: this will never be undefined + const incentive = incentives.at(i)!; + if (isTarget(incentive)) incentivesPayloads[i] = incentive; + else { + if (incentive.address) + incentivesPayloads[i] = { + isBase: incentive.isBase, + instance: incentive.address, + parameters: incentive.isBase + ? incentive.buildParameters(undefined, options).args.at(0) || + zeroHash + : zeroHash, + }; + else { + incentiveHashes[i] = await incentive.deploy(undefined, options); + } + } + } + + const [ + budgetAddress, + actionAddress, + validatorAddress, + allowListAddress, + incentiveAddresses, + ] = await Promise.all([ + budgetHash + ? waitForTransactionReceipt(options.config, { + hash: budgetHash, + }).then((contract) => contract.contractAddress) + : Promise.resolve(budgetPayload), + actionHash + ? waitForTransactionReceipt(options.config, { + hash: actionHash, + }).then((contract) => contract.contractAddress) + : Promise.resolve(actionPayload.instance), + validatorHash + ? waitForTransactionReceipt(options.config, { + hash: validatorHash, + }).then((contract) => contract.contractAddress) + : Promise.resolve(validatorPayload.instance), + allowListHash + ? waitForTransactionReceipt(options.config, { + hash: allowListHash, + }).then((contract) => contract.contractAddress) + : Promise.resolve(allowListPayload.instance), + Promise.all( + incentiveHashes.map((hash, i) => + hash + ? waitForTransactionReceipt(options.config, { + hash: hash, + }).then((contract) => contract.contractAddress) + : // biome-ignore lint/style/noNonNullAssertion: these incentive arrays will always be of the same length + Promise.resolve(incentivesPayloads.at(i)!.instance), + ), + ), + ]); + + if (budgetAddress) budgetPayload = budgetAddress; + if (actionAddress) actionPayload.instance = actionAddress; + if (validatorAddress) validatorPayload.instance = validatorAddress; + if (allowListAddress) allowListPayload.instance = allowListAddress; + for (let i = 0; i < incentiveAddresses.length; i++) { + // biome-ignore lint/style/noNonNullAssertion: these incentive arrays will always be of the same length + incentivesPayloads.at(i)!.instance = incentiveAddresses.at(i)!; + } + + const onChainPayload = { + budget: budgetPayload, + action: actionPayload, + validator: validatorPayload, + allowList: allowListPayload, + incentives: incentivesPayloads, + protocolFee, + referralFee, + maxParticipants, + owner, + }; + + const encodedBoost = await boostFactory(options.config, { + args: [prepareBoostPayload(onChainPayload)], + ...this.optionallyAttachAccount(options.account), + }); + + console.log(encodedBoost); + const values = decodeAbiParameters( + [ + { + components: [ + { + internalType: 'contract Action', + name: 'action', + type: 'address', + }, + { + internalType: 'contract Validator', + name: 'validator', + type: 'address', + }, + { + internalType: 'contract AllowList', + name: 'allowList', + type: 'address', + }, + { + internalType: 'contract Budget', + name: 'budget', + type: 'address', + }, + { + internalType: 'contract Incentive[]', + name: 'incentives', + type: 'address[]', + }, + { + internalType: 'uint64', + name: 'protocolFee', + type: 'uint64', + }, + { + internalType: 'uint64', + name: 'referralFee', + type: 'uint64', + }, + { + internalType: 'uint256', + name: 'maxParticipants', + type: 'uint256', + }, + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + internalType: 'struct BoostLib.Boost', + name: '', + type: 'tuple', + }, + ], + encodedBoost, + ); + + console.log(values); + } + + ContractAction( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new ContractAction( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + ERC721MintAction( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new ERC721MintAction( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + SimpleAllowList( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new SimpleAllowList( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + SimpleDenyList( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new SimpleDenyList( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + SimpleBudget(options: DeployablePayloadOrAddress) { + return new SimpleBudget( + { config: this._config, account: this._account }, + options, + ); + } + VestingBudget(options: DeployablePayloadOrAddress) { + return new VestingBudget( + { config: this._config, account: this._account }, + options, + ); + } + AllowListIncentive( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new AllowListIncentive( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + CGDAIncentive( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new CGDAIncentive( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + ERC20Incentive( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new ERC20Incentive( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + ERC1155Incentive( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new ERC1155Incentive( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + PointsIncentive( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new PointsIncentive( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + SignerValidator( + options: DeployablePayloadOrAddress, + isBase = false, + ) { + return new SignerValidator( + { config: this._config, account: this._account }, + options, + isBase, + ); + } + + public override buildParameters( + _payload?: [Address, Address], + _options?: DeployableOptions, + ): GenericDeployableParams { + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); + return { + abi: BoostCoreArtifact.abi, + bytecode: BoostCoreArtifact.bytecode as Hex, + args: payload, + ...this.optionallyAttachAccount(options.account), + }; + } +} diff --git a/packages/sdk/src/Budgets/Budget.ts b/packages/sdk/src/Budgets/Budget.ts index 44ec237a..4909f957 100644 --- a/packages/sdk/src/Budgets/Budget.ts +++ b/packages/sdk/src/Budgets/Budget.ts @@ -1,7 +1,4 @@ -import { - prepareERC1155Transfer, - prepareFungibleTransfer, -} from '../../../evm/artifacts'; +import { prepareERC1155Transfer, prepareFungibleTransfer } from '@boostxyz/evm'; import { SimpleBudget } from './SimpleBudget'; import { VestingBudget } from './VestingBudget'; diff --git a/packages/sdk/src/Budgets/SimpleBudget.ts b/packages/sdk/src/Budgets/SimpleBudget.ts index 6c46b935..8c909150 100644 --- a/packages/sdk/src/Budgets/SimpleBudget.ts +++ b/packages/sdk/src/Budgets/SimpleBudget.ts @@ -1,5 +1,3 @@ -import { type Config, getAccount } from '@wagmi/core'; -import { type Address, type Hex, zeroAddress } from 'viem'; import { type SimpleBudgetPayload, type TransferPayload, @@ -15,10 +13,13 @@ import { writeSimpleBudgetDisburseBatch, writeSimpleBudgetReclaim, writeSimpleBudgetSetAuthorized, -} from '../../../evm/artifacts'; -import SimpleBudgetArtifact from '../../../evm/artifacts/contracts/budgets/SimpleBudget.sol/SimpleBudget.json'; +} from '@boostxyz/evm'; +import SimpleBudgetArtifact from '@boostxyz/evm/artifacts/contracts/budgets/SimpleBudget.sol/SimpleBudget.json'; +import { getAccount } from '@wagmi/core'; +import { type Address, type Hex, zeroAddress } from 'viem'; import { Deployable, + type DeployableOptions, type GenericDeployableParams, } from '../Deployable/Deployable'; import { DeployableUnknownOwnerProvidedError } from '../errors'; @@ -141,11 +142,18 @@ export class SimpleBudget extends Deployable { public override buildParameters( _payload?: SimpleBudgetPayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload, config] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); if (!payload.owner || payload.owner === zeroAddress) { - const owner = getAccount(config).address; + const owner = options.account + ? options.account.address + : options.config + ? getAccount(options.config).address + : this._account?.address; if (owner) { payload.owner = owner; } else { @@ -156,6 +164,7 @@ export class SimpleBudget extends Deployable { abi: SimpleBudgetArtifact.abi, bytecode: SimpleBudgetArtifact.bytecode as Hex, args: [prepareSimpleBudgetPayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Budgets/VestingBudget.ts b/packages/sdk/src/Budgets/VestingBudget.ts index 5d38722f..abd82673 100644 --- a/packages/sdk/src/Budgets/VestingBudget.ts +++ b/packages/sdk/src/Budgets/VestingBudget.ts @@ -1,5 +1,3 @@ -import { type Config, getAccount } from '@wagmi/core'; -import { type Address, type Hex, zeroAddress } from 'viem'; import { type TransferPayload, type VestingBudgetPayload, @@ -15,10 +13,13 @@ import { writeVestingBudgetDisburseBatch, writeVestingBudgetReclaim, writeVestingBudgetSetAuthorized, -} from '../../../evm/artifacts'; -import VestingBudgetArtifact from '../../../evm/artifacts/contracts/budgets/VestingBudget.sol/VestingBudget.json'; +} from '@boostxyz/evm'; +import VestingBudgetArtifact from '@boostxyz/evm/artifacts/contracts/budgets/VestingBudget.sol/VestingBudget.json'; +import { type Config, getAccount } from '@wagmi/core'; +import { type Address, type Hex, zeroAddress } from 'viem'; import { Deployable, + type DeployableOptions, type GenericDeployableParams, } from '../Deployable/Deployable'; import { DeployableUnknownOwnerProvidedError } from '../errors'; @@ -143,11 +144,18 @@ export class VestingBudget extends Deployable { public override buildParameters( _payload?: VestingBudgetPayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload, config] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); if (!payload.owner || payload.owner === zeroAddress) { - const owner = getAccount(config).address; + const owner = options.account + ? options.account.address + : options.config + ? getAccount(options.config).address + : this._account?.address; if (owner) { payload.owner = owner; } else { @@ -158,6 +166,7 @@ export class VestingBudget extends Deployable { abi: VestingBudgetArtifact.abi, bytecode: VestingBudgetArtifact.bytecode as Hex, args: [prepareVestingBudgetPayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Deployable/Contract.ts b/packages/sdk/src/Deployable/Contract.ts index 52a9a77d..9975994c 100644 --- a/packages/sdk/src/Deployable/Contract.ts +++ b/packages/sdk/src/Deployable/Contract.ts @@ -1,5 +1,5 @@ import type { Config } from '@wagmi/core'; -import type { Account, Address } from 'viem'; +import type { Address } from 'viem'; import { ContractAddressRequiredError } from '../errors'; export class Contract { diff --git a/packages/sdk/src/Deployable/Deployable.ts b/packages/sdk/src/Deployable/Deployable.ts index e8a14e47..d53aebda 100644 --- a/packages/sdk/src/Deployable/Deployable.ts +++ b/packages/sdk/src/Deployable/Deployable.ts @@ -1,7 +1,7 @@ import { + type Config, type Config as WagmiConfig, deployContract, - getClient, } from '@wagmi/core'; import type { Account, Address, Hash, Hex } from 'viem'; import { @@ -19,9 +19,10 @@ export type GenericDeployableParams = Omit< args: [Hex, ...Array]; }; -export type DeployableOptions = Payload | Address; +export type DeployablePayloadOrAddress = Payload | Address; -export interface DeployableConfig extends WagmiConfig { +export interface DeployableOptions { + config: Config; account?: Account; } export class Deployable extends Contract { @@ -29,14 +30,14 @@ export class Deployable extends Contract { protected _account?: Account; constructor( - { account, ...config }: DeployableConfig, - options: DeployableOptions, + { account, config }: DeployableOptions, + payload: DeployablePayloadOrAddress, ) { - if (typeof options === 'string') { - super(config, options as Address); + if (typeof payload === 'string') { + super(config, payload as Address); } else { super(config, undefined); - this._payload = options as Payload; + this._payload = payload as Payload; } if (account) this._account = account; } @@ -52,33 +53,40 @@ export class Deployable extends Contract { public async deploy( _payload?: Payload, - _config?: DeployableConfig, + _options?: DeployableOptions, ): Promise { if (this.address) throw new DeployableAlreadyDeployedError(this.address); - const config = _config || this._config; const payload = _payload || this._payload; - const account = _config?.account || this._account; + const config = _options?.config || this._config; return await deployContract(config, { ...this.buildParameters(payload), - ...(account ? { account } : {}), + ...this.optionallyAttachAccount(_options?.account), }); } - public buildParameters( + protected optionallyAttachAccount(account?: Account) { + if (account) return { account }; + return this._account ? { account: this._account } : {}; + } + + protected buildParameters( _payload?: Payload, - _config?: DeployableConfig, + _options?: DeployableOptions, ): GenericDeployableParams { throw new DeployableBuildParametersUnspecifiedError(); } - public validateDeploymentConfig( - _payload?: Payload, - _config?: DeployableConfig, + protected validateDeploymentConfig

( + _payload?: P, + _options?: DeployableOptions, ) { - const config = _config || this._config; - if (!config) throw new DeployableWagmiConfigurationRequiredError(); + const options = _options || { + config: this._config, + account: this._account, + }; + if (!options) throw new DeployableWagmiConfigurationRequiredError(); const payload = _payload || this._payload; if (!payload) throw new DeployableMissingPayloadError(); - return [payload, config] as [Payload, WagmiConfig]; + return [payload, options] as [P, DeployableOptions]; } } diff --git a/packages/sdk/src/Deployable/DeployableTarget.ts b/packages/sdk/src/Deployable/DeployableTarget.ts new file mode 100644 index 00000000..704159c1 --- /dev/null +++ b/packages/sdk/src/Deployable/DeployableTarget.ts @@ -0,0 +1,17 @@ +import { + Deployable, + type DeployableOptions, + type DeployablePayloadOrAddress, +} from './Deployable'; + +export class DeployableTarget extends Deployable { + readonly isBase: boolean; + constructor( + options: DeployableOptions, + payload: DeployablePayloadOrAddress, + isBase = false, + ) { + super(options, payload); + this.isBase = isBase; + } +} diff --git a/packages/sdk/src/Incentives/AllowListIncentive.ts b/packages/sdk/src/Incentives/AllowListIncentive.ts index a5227a44..16294863 100644 --- a/packages/sdk/src/Incentives/AllowListIncentive.ts +++ b/packages/sdk/src/Incentives/AllowListIncentive.ts @@ -1,5 +1,3 @@ -import type { Config } from '@wagmi/core'; -import type { Hex } from 'viem'; import { type AllowListIncentivePayload, type ClaimPayload, @@ -9,18 +7,20 @@ import { readAllowListIncentiveIsClaimable, readAllowListIncentiveLimit, writeAllowListIncentiveClaim, -} from '../../../evm/artifacts'; -import AllowListIncentiveArtifact from '../../../evm/artifacts/contracts/incentives/AllowListIncentive.sol/AllowListIncentive.json'; +} from '@boostxyz/evm'; +import AllowListIncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/AllowListIncentive.sol/AllowListIncentive.json'; +import type { Hex } from 'viem'; import { SimpleAllowList } from '../AllowLists/AllowList'; -import { - Deployable, - type GenericDeployableParams, +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import type { CallParams } from '../utils'; export type { AllowListIncentivePayload }; -export class AllowListIncentive extends Deployable { +export class AllowListIncentive extends DeployableTarget { public async allowList( params: CallParams = {}, ): Promise { @@ -28,7 +28,10 @@ export class AllowListIncentive extends Deployable { address: this.assertValidAddress(), ...params, }); - return new SimpleAllowList(this._config, address); + return new SimpleAllowList( + { config: this._config, account: this._account }, + address, + ); } public async limit( @@ -66,13 +69,17 @@ export class AllowListIncentive extends Deployable { public override buildParameters( _payload?: AllowListIncentivePayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); return { abi: AllowListIncentiveArtifact.abi, bytecode: AllowListIncentiveArtifact.bytecode as Hex, args: [prepareAllowListIncentivePayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Incentives/CGDAIncentive.ts b/packages/sdk/src/Incentives/CGDAIncentive.ts index f482826e..319300ac 100644 --- a/packages/sdk/src/Incentives/CGDAIncentive.ts +++ b/packages/sdk/src/Incentives/CGDAIncentive.ts @@ -1,5 +1,3 @@ -import type { Config } from '@wagmi/core'; -import type { Hex } from 'viem'; import { type CGDAIncentivePayload, type CGDAParameters, @@ -13,17 +11,19 @@ import { readCgdaIncentiveTotalBudget, writeCgdaIncentiveClaim, writeCgdaIncentiveReclaim, -} from '../../../evm/artifacts'; -import CGDAIncentiveArtifact from '../../../evm/artifacts/contracts/incentives/CGDAIncentive.sol/CGDAIncentive.json'; -import { - Deployable, - type GenericDeployableParams, +} from '@boostxyz/evm'; +import CGDAIncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/CGDAIncentive.sol/CGDAIncentive.json'; +import type { Hex } from 'viem'; +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import type { CallParams } from '../utils'; export type { CGDAIncentivePayload }; -export class CGDAIncentive extends Deployable { +export class CGDAIncentive extends DeployableTarget { public async asset(params: CallParams = {}) { return readCgdaIncentiveAsset(this._config, { address: this.assertValidAddress(), @@ -113,13 +113,17 @@ export class CGDAIncentive extends Deployable { public override buildParameters( _payload?: CGDAIncentivePayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); return { abi: CGDAIncentiveArtifact.abi, bytecode: CGDAIncentiveArtifact.bytecode as Hex, args: [prepareCGDAIncentivePayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Incentives/ERC1155Incentive.ts b/packages/sdk/src/Incentives/ERC1155Incentive.ts index 6d312aeb..550e0bae 100644 --- a/packages/sdk/src/Incentives/ERC1155Incentive.ts +++ b/packages/sdk/src/Incentives/ERC1155Incentive.ts @@ -1,5 +1,3 @@ -import type { Config } from '@wagmi/core'; -import type { Hex } from 'viem'; import { type ClaimPayload, type ERC1155IncentivePayload, @@ -16,17 +14,19 @@ import { readErc1155SupportsInterface, writeErc1155IncentiveClaim, writeErc1155IncentiveReclaim, -} from '../../../evm/artifacts'; -import ERC1155IncentiveArtifact from '../../../evm/artifacts/contracts/incentives/ERC1155Incentive.sol/ERC1155Incentive.json'; -import { - Deployable, - type GenericDeployableParams, +} from '@boostxyz/evm'; +import ERC1155IncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/ERC1155Incentive.sol/ERC1155Incentive.json'; +import type { Hex } from 'viem'; +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import type { CallParams } from '../utils'; export type { ERC1155IncentivePayload }; -export class ERC1155Incentive extends Deployable { +export class ERC1155Incentive extends DeployableTarget { public async asset( params: CallParams = {}, ) { @@ -129,13 +129,17 @@ export class ERC1155Incentive extends Deployable { public override buildParameters( _payload?: ERC1155IncentivePayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); return { abi: ERC1155IncentiveArtifact.abi, bytecode: ERC1155IncentiveArtifact.bytecode as Hex, args: [prepareERC1155IncentivePayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Incentives/ERC20Incentive.ts b/packages/sdk/src/Incentives/ERC20Incentive.ts index bd2a2d0c..7bc01703 100644 --- a/packages/sdk/src/Incentives/ERC20Incentive.ts +++ b/packages/sdk/src/Incentives/ERC20Incentive.ts @@ -1,5 +1,3 @@ -import type { Config } from '@wagmi/core'; -import type { Hex } from 'viem'; import { type ClaimPayload, type ERC20IncentivePayload, @@ -16,17 +14,20 @@ import { writeErc20IncentiveClaim, writeErc20IncentiveDrawRaffle, writeErc20IncentiveReclaim, -} from '../../../evm/artifacts'; -import ERC20IncentiveArtifact from '../../../evm/artifacts/contracts/incentives/ERC20Incentive.sol/ERC20Incentive.json'; +} from '@boostxyz/evm'; +import ERC20IncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/ERC20Incentive.sol/ERC20Incentive.json'; +import type { Hex } from 'viem'; import { Deployable, + type DeployableOptions, type GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import type { CallParams } from '../utils'; export type { ERC20IncentivePayload }; -export class ERC20Incentive extends Deployable { +export class ERC20Incentive extends DeployableTarget { public async asset(params: CallParams = {}) { return readErc20IncentiveAsset(this._config, { address: this.assertValidAddress(), @@ -127,13 +128,17 @@ export class ERC20Incentive extends Deployable { public override buildParameters( _payload?: ERC20IncentivePayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); return { abi: ERC20IncentiveArtifact.abi, bytecode: ERC20IncentiveArtifact.bytecode as Hex, args: [prepareERC20IncentivePayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Incentives/PointsIncentive.ts b/packages/sdk/src/Incentives/PointsIncentive.ts index 72e7b195..ccb43c16 100644 --- a/packages/sdk/src/Incentives/PointsIncentive.ts +++ b/packages/sdk/src/Incentives/PointsIncentive.ts @@ -1,5 +1,3 @@ -import type { Config } from '@wagmi/core'; -import type { Hex } from 'viem'; import { type ClaimPayload, type PointsIncentivePayload, @@ -11,17 +9,19 @@ import { readPointsIncentiveSelector, readPointsIncentiveVenue, writePointsIncentiveClaim, -} from '../../../evm/artifacts'; -import PointsIncentiveArtifact from '../../../evm/artifacts/contracts/incentives/PointsIncentive.sol/PointsIncentive.json'; -import { - Deployable, - type GenericDeployableParams, +} from '@boostxyz/evm'; +import PointsIncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/PointsIncentive.sol/PointsIncentive.json'; +import type { Hex } from 'viem'; +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import type { CallParams } from '../utils'; export type { PointsIncentivePayload }; -export class PointsIncentive extends Deployable { +export class PointsIncentive extends DeployableTarget { public async venue(params: CallParams = {}) { return readPointsIncentiveVenue(this._config, { address: this.assertValidAddress(), @@ -80,13 +80,17 @@ export class PointsIncentive extends Deployable { public override buildParameters( _payload?: PointsIncentivePayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); return { abi: PointsIncentiveArtifact.abi, bytecode: PointsIncentiveArtifact.bytecode as Hex, args: [preparePointsIncentivePayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/Validators/SignerValidator.ts b/packages/sdk/src/Validators/SignerValidator.ts index 12c41db6..76ba1f59 100644 --- a/packages/sdk/src/Validators/SignerValidator.ts +++ b/packages/sdk/src/Validators/SignerValidator.ts @@ -1,25 +1,24 @@ -import type { Config } from '@wagmi/core'; -import type { Address, Hex } from 'viem'; import { type SignerValidatorPayload, type SignerValidatorValidatePayload, - type SimpleAllowListPayload, prepareSignerValidatorPayload, prepareSignerValidatorValidatePayload, readSignerValidatorSigners, writeSignerValidatorSetAuthorized, writeSignerValidatorValidate, -} from '../../../evm/artifacts'; -import SignerValidatorArtifact from '../../../evm/artifacts/contracts/validators/SignerValidator.sol/SignerValidator.json'; -import { - Deployable, - type GenericDeployableParams, +} from '@boostxyz/evm'; +import SignerValidatorArtifact from '@boostxyz/evm/artifacts/contracts/validators/SignerValidator.sol/SignerValidator.json'; +import type { Address, Hex } from 'viem'; +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; +import { DeployableTarget } from '../Deployable/DeployableTarget'; import type { CallParams } from '../utils'; export type { SignerValidatorPayload }; -export class SignerValidator extends Deployable { +export class SignerValidator extends DeployableTarget { public async signers( address: Address, params: CallParams = {}, @@ -56,13 +55,17 @@ export class SignerValidator extends Deployable { public override buildParameters( _payload?: SignerValidatorPayload, - _config?: Config, + _options?: DeployableOptions, ): GenericDeployableParams { - const [payload] = this.validateDeploymentConfig(_payload, _config); + const [payload, options] = this.validateDeploymentConfig( + _payload, + _options, + ); return { abi: SignerValidatorArtifact.abi, bytecode: SignerValidatorArtifact.bytecode as Hex, args: [prepareSignerValidatorPayload(payload)], + ...this.optionallyAttachAccount(options.account), }; } } diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index d6f803fb..fe80af53 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,4 +1,4 @@ -export * from './BoostClient'; +export * from './BoostCore'; export * from './Boost'; export * from './errors'; diff --git a/packages/sdk/test/helpers.ts b/packages/sdk/test/helpers.ts index 40c9b296..99594ad8 100644 --- a/packages/sdk/test/helpers.ts +++ b/packages/sdk/test/helpers.ts @@ -18,12 +18,6 @@ import type { Address, Hex } from 'viem'; import { accounts } from './accounts'; import { mockWalletClient, setupConfig, testAccount } from './viem'; -export interface Fixtures { - bases: { type: RegistryType; name: string; base: Address }[]; - core: Address; - registry: Address; -} - export async function deployContract( client: typeof mockWalletClient, ...params: Parameters @@ -34,10 +28,12 @@ export async function deployContract( return tx.contractAddress as Address; } +export type Fixtures = Awaited>; + export async function deployFixtures( - account: Address = accounts.at(0)!.account, + account: Address = accounts.at(0)?.account, client = mockWalletClient, -): Promise { +) { const registry = await deployContract(client, { abi: BoostRegistry.abi, bytecode: BoostRegistry.bytecode as Hex, @@ -51,8 +47,8 @@ export async function deployFixtures( args: [registry, account], }); - const bases = [ - { + const bases = { + ContractAction: { type: RegistryType.ACTION, name: 'ContractAction', base: await deployContract(client, { @@ -61,7 +57,7 @@ export async function deployFixtures( account, }), }, - { + ERC721MintAction: { type: RegistryType.ACTION, name: 'ERC721MintAction', base: await deployContract(client, { @@ -70,7 +66,7 @@ export async function deployFixtures( account, }), }, - { + SimpleAllowList: { type: RegistryType.ALLOW_LIST, name: 'SimpleAllowList', base: await deployContract(client, { @@ -79,7 +75,7 @@ export async function deployFixtures( account, }), }, - { + SimpleDenyList: { type: RegistryType.ALLOW_LIST, name: 'SimpleDenyList', base: await deployContract(client, { @@ -88,7 +84,7 @@ export async function deployFixtures( account, }), }, - { + SimpleBudget: { type: RegistryType.BUDGET, name: 'SimpleBudget', base: await deployContract(client, { @@ -97,7 +93,7 @@ export async function deployFixtures( account, }), }, - { + VestingBudget: { type: RegistryType.BUDGET, name: 'VestingBudget', base: await deployContract(client, { @@ -106,7 +102,7 @@ export async function deployFixtures( account, }), }, - { + AllowListIncentive: { type: RegistryType.INCENTIVE, name: 'AllowListIncentive', base: await deployContract(client, { @@ -115,7 +111,7 @@ export async function deployFixtures( account, }), }, - { + CGDAIncentive: { type: RegistryType.INCENTIVE, name: 'CGDAIncentive', base: await deployContract(client, { @@ -124,7 +120,7 @@ export async function deployFixtures( account, }), }, - { + ERC20Incentive: { type: RegistryType.INCENTIVE, name: 'ERC20Incentive', base: await deployContract(client, { @@ -133,7 +129,7 @@ export async function deployFixtures( account, }), }, - { + ERC1155Incentive: { type: RegistryType.INCENTIVE, name: 'ERC1155Incentive', base: await deployContract(client, { @@ -142,7 +138,7 @@ export async function deployFixtures( account, }), }, - { + PointsIncentive: { type: RegistryType.INCENTIVE, name: 'PointsIncentive', base: await deployContract(client, { @@ -151,7 +147,7 @@ export async function deployFixtures( account, }), }, - { + SignerValidator: { type: RegistryType.VALIDATOR, name: 'SignerValidator', base: await deployContract(client, { @@ -160,11 +156,11 @@ export async function deployFixtures( account, }), }, - ]; + }; const config = setupConfig(client); - for (const { type, name, base } of bases) { + for (const { type, name, base } of Object.values(bases)) { await writeBoostRegistryRegister(config, { account: { ...testAccount, type: 'local' }, address: registry, diff --git a/packages/sdk/test/viem.ts b/packages/sdk/test/viem.ts index 2b6ebc61..86550f05 100644 --- a/packages/sdk/test/viem.ts +++ b/packages/sdk/test/viem.ts @@ -1,11 +1,22 @@ import { createConfig } from '@wagmi/core'; -import { http, createTestClient, publicActions, walletActions } from 'viem'; -import { hardhat } from 'viem/chains'; - +import { + http, + createTestClient, + publicActions, + walletActions, + zeroAddress, + zeroHash, +} from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; +import { hardhat } from 'viem/chains'; import { accounts } from './accounts'; -const { account, key } = accounts.at(0)!; +const { account, key } = accounts.at(0) || { + account: zeroAddress, + key: zeroHash, +}; + +export { account, key }; export const testAccount = privateKeyToAccount(key); export const mockWalletClient = createTestClient({