diff --git a/packages/sdk/src/Actions/ContractAction.ts b/packages/sdk/src/Actions/ContractAction.ts index 7085d2b97..b5924e3fd 100644 --- a/packages/sdk/src/Actions/ContractAction.ts +++ b/packages/sdk/src/Actions/ContractAction.ts @@ -1,15 +1,18 @@ import { type ContractActionPayload, + contractActionAbi, prepareContractActionPayload, readContractActionChainId, readContractActionPrepare, readContractActionSelector, readContractActionTarget, readContractActionValue, + simulateContractActionExecute, writeContractActionExecute, } from '@boostxyz/evm'; -import ContractActionArtifact from '@boostxyz/evm/artifacts/contracts/actions/ContractAction.sol/ContractAction.json'; -import type { Hex } from 'viem'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/actions/ContractAction.sol/ContractAction.json'; +import { getTransaction, waitForTransactionReceipt } from '@wagmi/core'; +import { type Hex, decodeFunctionData } from 'viem'; import type { DeployableOptions, GenericDeployableParams, @@ -61,6 +64,17 @@ export class ContractAction extends DeployableTarget { public async execute( data: Hex, params: CallParams = {}, + ) { + return this.awaitResult( + this.executeRaw(data, params), + contractActionAbi, + simulateContractActionExecute, + ); + } + + public async executeRaw( + data: Hex, + params: CallParams = {}, ) { return writeContractActionExecute(this._config, { address: this.assertValidAddress(), @@ -91,8 +105,8 @@ export class ContractAction extends DeployableTarget { _options, ); return { - abi: ContractActionArtifact.abi, - bytecode: ContractActionArtifact.bytecode as Hex, + abi: contractActionAbi, + bytecode: 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 1be39e81b..e2aefa016 100644 --- a/packages/sdk/src/Actions/ERC721MintAction.ts +++ b/packages/sdk/src/Actions/ERC721MintAction.ts @@ -1,17 +1,18 @@ import { type ERC721MintActionPayload, + erc721MintActionAbi, prepareERC721MintActionPayload, readErc721MintActionPrepare, + simulateErc721MintActionExecute, + simulateErc721MintActionValidate, writeErc721MintActionExecute, writeErc721MintActionValidate, } from '@boostxyz/evm'; -import ERC721MintActionArtifact from '@boostxyz/evm/artifacts/contracts/actions/ERC721MintAction.sol/ERC721MintAction.json'; -import type { Config } from '@wagmi/core'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/actions/ERC721MintAction.sol/ERC721MintAction.json'; import type { Hex } from 'viem'; -import { - Deployable, - type DeployableOptions, - type GenericDeployableParams, +import type { + DeployableOptions, + GenericDeployableParams, } from '../Deployable/Deployable'; import type { CallParams } from '../utils'; import { ContractAction } from './ContractAction'; @@ -22,6 +23,17 @@ export class ERC721MintAction extends ContractAction { public override async execute( data: Hex, params: CallParams = {}, + ) { + return this.awaitResult( + this.executeRaw(data, params), + erc721MintActionAbi, + simulateErc721MintActionExecute, + ); + } + + public override async executeRaw( + data: Hex, + params: CallParams = {}, ) { return writeErc721MintActionExecute(this._config, { address: this.assertValidAddress(), @@ -46,6 +58,17 @@ export class ERC721MintAction extends ContractAction { public async validate( data: Hex, params: CallParams = {}, + ) { + return this.awaitResult( + this.validateRaw(data, params), + erc721MintActionAbi, + simulateErc721MintActionValidate, + ); + } + + public async validateRaw( + data: Hex, + params: CallParams = {}, ) { return writeErc721MintActionValidate(this._config, { address: this.assertValidAddress(), @@ -64,8 +87,8 @@ export class ERC721MintAction extends ContractAction { _options, ); return { - abi: ERC721MintActionArtifact.abi, - bytecode: ERC721MintActionArtifact.bytecode as Hex, + abi: erc721MintActionAbi, + bytecode: 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 8c9d08076..4ee0f97c8 100644 --- a/packages/sdk/src/AllowLists/SimpleAllowList.ts +++ b/packages/sdk/src/AllowLists/SimpleAllowList.ts @@ -2,9 +2,11 @@ import { type SimpleAllowListPayload, prepareSimpleAllowListPayload, readSimpleAllowListIsAllowed, + simpleAllowListAbi, + simulateSimpleAllowListSetAllowed, writeSimpleAllowListSetAllowed, } from '@boostxyz/evm'; -import SimpleAllowListArtifact from '@boostxyz/evm/artifacts/contracts/allowlists/SimpleAllowList.sol/SimpleAllowList.json'; +import { bytecode } 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 { @@ -33,7 +35,19 @@ export class SimpleAllowList extends DeployableTarget { public async setAllowed( addresses: Address[], allowed: boolean[], - params: CallParams = {}, + params: CallParams = {}, + ) { + return this.awaitResult( + this.setAllowedRaw(addresses, allowed, params), + simpleAllowListAbi, + simulateSimpleAllowListSetAllowed, + ); + } + + public async setAllowedRaw( + addresses: Address[], + allowed: boolean[], + params: CallParams = {}, ) { return await writeSimpleAllowListSetAllowed(this._config, { address: this.assertValidAddress(), @@ -41,6 +55,7 @@ export class SimpleAllowList extends DeployableTarget { ...params, }); } + public override buildParameters( _payload?: SimpleAllowListPayload, _options?: DeployableOptions, @@ -62,8 +77,8 @@ export class SimpleAllowList extends DeployableTarget { } } return { - abi: SimpleAllowListArtifact.abi, - bytecode: SimpleAllowListArtifact.bytecode as Hex, + abi: simpleAllowListAbi, + bytecode: 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 7ee22aa3e..7c89d8697 100644 --- a/packages/sdk/src/AllowLists/SimpleDenyList.ts +++ b/packages/sdk/src/AllowLists/SimpleDenyList.ts @@ -2,9 +2,11 @@ import { type SimpleDenyListPayload, prepareSimpleDenyListPayload, readSimpleDenyListIsAllowed, + simpleDenyListAbi, + simulateSimpleDenyListSetDenied, writeSimpleDenyListSetDenied, } from '@boostxyz/evm'; -import SimpleDenyListArtifact from '@boostxyz/evm/artifacts/contracts/allowlists/SimpleDenyList.sol/SimpleDenyList.json'; +import { bytecode } 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 { @@ -33,6 +35,18 @@ export class SimpleDenyList extends DeployableTarget { addresses: Address[], allowed: boolean[], params: CallParams = {}, + ) { + return this.awaitResult( + this.setAllowedRaw(addresses, allowed, params), + simpleDenyListAbi, + simulateSimpleDenyListSetDenied, + ); + } + + public async setAllowedRaw( + addresses: Address[], + allowed: boolean[], + params: CallParams = {}, ) { return await writeSimpleDenyListSetDenied(this._config, { address: this.assertValidAddress(), @@ -62,8 +76,8 @@ export class SimpleDenyList extends DeployableTarget { } } return { - abi: SimpleDenyListArtifact.abi, - bytecode: SimpleDenyListArtifact.bytecode as Hex, + abi: simpleDenyListAbi, + bytecode: 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 7e6cd04a2..cbfc4076a 100644 --- a/packages/sdk/src/Boost.ts +++ b/packages/sdk/src/Boost.ts @@ -5,8 +5,7 @@ import type { Budget } from './Budgets/Budget'; import type { Incentive } from './Incentives/Incentive'; import type { Validator } from './Validators/Validator'; -export type BoostPayload = { - address?: Address; +export interface BoostPayload { budget: Budget; action: Action; validator: Validator; @@ -16,13 +15,13 @@ export type BoostPayload = { referralFee?: bigint; maxParticipants?: bigint; owner?: Address; -}; +} export class Boost { + readonly budget: Budget; readonly action: Action; readonly validator: Validator; readonly allowList: AllowList; - readonly budget: Budget; readonly incentives: Array; readonly protocolFee: bigint; readonly referralFee: bigint; @@ -30,10 +29,10 @@ export class Boost { readonly owner: Address; constructor(payload: BoostPayload) { + this.budget = payload.budget; this.action = payload.action; this.validator = payload.validator; this.allowList = payload.allowList; - this.budget = payload.budget; this.incentives = payload.incentives; this.protocolFee = payload.protocolFee || 0n; this.referralFee = payload.referralFee || 0n; diff --git a/packages/sdk/src/BoostClient.test.ts b/packages/sdk/src/BoostClient.test.ts deleted file mode 100644 index 0271a3d1e..000000000 --- a/packages/sdk/src/BoostClient.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -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, - config = setupConfig(); - -beforeAll(async () => { - fixtures = await deployFixtures(); -}); - -test('expect true', async () => { - console.log(fixtures); - 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/BoostCore.test.ts b/packages/sdk/src/BoostCore.test.ts new file mode 100644 index 000000000..fe06e6f90 --- /dev/null +++ b/packages/sdk/src/BoostCore.test.ts @@ -0,0 +1,59 @@ +import { beforeAll, describe, test } from 'vitest'; +import { type Fixtures, deployFixtures } from '../test/helpers'; +import { setupConfig, testAccount } from '../test/viem'; +import { BoostCore } from './BoostCore'; + +let fixtures: Fixtures; + +beforeAll(async () => { + fixtures = await deployFixtures(); +}); + +describe('BoostCore', () => { + test('can successfully create a boost', async () => { + const config = setupConfig(); + 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/BoostCore.ts b/packages/sdk/src/BoostCore.ts index 5e9c4aa90..018d8b234 100644 --- a/packages/sdk/src/BoostCore.ts +++ b/packages/sdk/src/BoostCore.ts @@ -1,11 +1,15 @@ import BoostCoreArtifact from '@boostxyz/evm/artifacts/contracts/BoostCore.sol/BoostCore.json'; -import { getAccount, waitForTransactionReceipt } from '@wagmi/core'; +import { + getAccount, + getTransaction, + waitForTransactionReceipt, +} from '@wagmi/core'; import { createWriteContract } from '@wagmi/core/codegen'; import { type Address, type Hash, type Hex, - decodeAbiParameters, + decodeFunctionData, zeroAddress, zeroHash, } from 'viem'; @@ -14,6 +18,7 @@ import { type Target, boostCoreAbi, prepareBoostPayload, + simulateBoostCoreCreateBoost, } from '../../evm/artifacts'; import type { Action } from './Actions/Action'; @@ -34,6 +39,7 @@ import { SimpleDenyList, type SimpleDenyListPayload, } from './AllowLists/SimpleDenyList'; +import { Boost } from './Boost'; import type { Budget } from './Budgets/Budget'; import { SimpleBudget, type SimpleBudgetPayload } from './Budgets/SimpleBudget'; import { @@ -100,27 +106,18 @@ export type BoostClientConfig = | BoostCoreDeployedOptions | BoostCoreOptionsWithPayload; -export type CreatBoostPayload = { - budget: Budget | Address; - action: Action | Target; - validator: Validator | Target; - allowList: AllowList | Target; - incentives: Array; +export type CreateBoostPayload = { + budget: Budget; + action: Action; + validator: Validator; + allowList: AllowList; + 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) { @@ -137,16 +134,17 @@ export class BoostCore extends Deployable<[Address, Address]> { // TODO make this transactional? if any deployment fails what do we do with the previously deployed deployables? public async createBoost( - _boostPayload: CreatBoostPayload, + _boostPayload: CreateBoostPayload, _options: DeployableOptions = { config: this._config, account: this._account, }, ) { - const [payload, options] = this.validateDeploymentConfig( - _boostPayload, - _options, - ); + const [payload, options] = + this.validateDeploymentConfig( + _boostPayload, + _options, + ); let { budget, @@ -157,7 +155,7 @@ export class BoostCore extends Deployable<[Address, Address]> { protocolFee = 0n, referralFee = 0n, maxParticipants = 0n, - owner = zeroAddress, + owner, } = payload; const boostFactory = createWriteContract({ @@ -186,79 +184,60 @@ export class BoostCore extends Deployable<[Address, Address]> { } } - console.log(JSON.stringify({ budgetPayload })); - let actionPayload: OnChainBoostPayload['action'] = { instance: zeroAddress, isBase: false, parameters: zeroHash, }, actionHash: Hash | undefined = undefined; - if (isTarget(action)) actionPayload = action; + if (action.address) + actionPayload = { + isBase: action.isBase, + instance: action.address, + parameters: action.isBase + ? action.buildParameters(undefined, options).args.at(0) || zeroHash + : zeroHash, + }; 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); - } + 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; + if (validator.address) + validatorPayload = { + isBase: validator.isBase, + instance: validator.address, + parameters: validator.isBase + ? validator.buildParameters(undefined, options).args.at(0) || zeroHash + : zeroHash, + }; 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); - } + 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; + if (allowList.address) + allowListPayload = { + isBase: allowList.isBase, + instance: allowList.address, + parameters: allowList.isBase + ? allowList.buildParameters(undefined, options).args.at(0) || zeroHash + : zeroHash, + }; 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); - } + allowListHash = await allowList.deploy(undefined, options); } - console.log(JSON.stringify({ allowListPayload })); - let incentivesPayloads: Array = incentives.map(() => ({ instance: zeroAddress, isBase: false, @@ -268,20 +247,17 @@ export class BoostCore extends Deployable<[Address, Address]> { 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; + if (incentive.address) + incentivesPayloads[i] = { + isBase: incentive.isBase, + instance: incentive.address, + parameters: incentive.isBase + ? incentive.buildParameters(undefined, options).args.at(0) || + zeroHash + : zeroHash, + }; 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); - } + incentiveHashes[i] = await incentive.deploy(undefined, options); } } @@ -345,71 +321,40 @@ export class BoostCore extends Deployable<[Address, Address]> { owner, }; - const encodedBoost = await boostFactory(options.config, { + const boostHash = await boostFactory(options.config, { args: [prepareBoostPayload(onChainPayload)], ...this.optionallyAttachAccount(options.account), }); + await waitForTransactionReceipt(options.config, { + hash: boostHash, + }); + const tx = await getTransaction(options.config, { + hash: boostHash, + }); + const { args } = decodeFunctionData({ + abi: boostCoreAbi, + data: tx.input, + }); + const { result } = await simulateBoostCoreCreateBoost(options.config, { + address: this.address!, + args: args as [Hex], + ...this.optionallyAttachAccount(), + }); - 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); + // TODO we need to figure out how to ensure the boost has the correct component instances, ie SimpleAllowList vs SimpleDenyList + return new Boost({ + budget: budget.at(result.budget), + action: action.at(result.action), + validator: validator.at(result.validator), + allowList: allowList.at(result.allowList), + incentives: incentives.map((incentive, i) => + incentive.at(result.incentives.at(i)!), + ), + protocolFee: result.protocolFee, + referralFee: result.referralFee, + maxParticipants: result.maxParticipants, + owner: result.owner, + }); } ContractAction( @@ -525,7 +470,7 @@ export class BoostCore extends Deployable<[Address, Address]> { ); } - public override buildParameters( + protected override buildParameters( _payload?: [Address, Address], _options?: DeployableOptions, ): GenericDeployableParams { diff --git a/packages/sdk/src/Budgets/SimpleBudget.ts b/packages/sdk/src/Budgets/SimpleBudget.ts index 8c9091509..fc1da5be8 100644 --- a/packages/sdk/src/Budgets/SimpleBudget.ts +++ b/packages/sdk/src/Budgets/SimpleBudget.ts @@ -8,13 +8,19 @@ import { readSimpleBudgetIsAuthorized, readSimpleBudgetTotal, readVestingBudgetStart, + simpleAllowListAbi, + simulateSimpleBudgetAllocate, + simulateSimpleBudgetDisburse, + simulateSimpleBudgetDisburseBatch, + simulateSimpleBudgetReclaim, + simulateSimpleBudgetSetAuthorized, writeSimpleBudgetAllocate, writeSimpleBudgetDisburse, writeSimpleBudgetDisburseBatch, writeSimpleBudgetReclaim, writeSimpleBudgetSetAuthorized, } from '@boostxyz/evm'; -import SimpleBudgetArtifact from '@boostxyz/evm/artifacts/contracts/budgets/SimpleBudget.sol/SimpleBudget.json'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/budgets/SimpleBudget.sol/SimpleBudget.json'; import { getAccount } from '@wagmi/core'; import { type Address, type Hex, zeroAddress } from 'viem'; import { @@ -35,7 +41,18 @@ export class SimpleBudget extends Deployable { }); } - public allocate( + public async allocate( + transfer: TransferPayload, + params: CallParams = {}, + ) { + return this.awaitResult( + this.allocateRaw(transfer, params), + simpleAllowListAbi, + simulateSimpleBudgetAllocate, + ); + } + + public allocateRaw( transfer: TransferPayload, params: CallParams = {}, ) { @@ -46,7 +63,18 @@ export class SimpleBudget extends Deployable { }); } - public reclaim( + public async reclaim( + transfer: TransferPayload, + params: CallParams = {}, + ) { + return this.awaitResult( + this.reclaimRaw(transfer, params), + simpleAllowListAbi, + simulateSimpleBudgetReclaim, + ); + } + + public reclaimRaw( transfer: TransferPayload, params: CallParams = {}, ) { @@ -57,7 +85,18 @@ export class SimpleBudget extends Deployable { }); } - public disburse( + public async disburse( + transfer: TransferPayload, + params: CallParams = {}, + ) { + return this.awaitResult( + this.disburseRaw(transfer, params), + simpleAllowListAbi, + simulateSimpleBudgetDisburse, + ); + } + + public disburseRaw( transfer: TransferPayload, params: CallParams = {}, ) { @@ -68,9 +107,20 @@ export class SimpleBudget extends Deployable { }); } + public async disburseBatch( + transfers: TransferPayload[], + params: CallParams = {}, + ) { + return this.awaitResult( + this.disburseBatchRaw(transfers, params), + simpleAllowListAbi, + simulateSimpleBudgetDisburseBatch, + ); + } + // use prepareFungibleTransfer or prepareERC1155Transfer // TODO use data structure - public disburseBatch( + public disburseBatchRaw( transfers: TransferPayload[], params: CallParams = {}, ) { @@ -85,6 +135,18 @@ export class SimpleBudget extends Deployable { addresses: Address[], allowed: boolean[], params: CallParams = {}, + ) { + return this.awaitResult( + this.setAuthorizedRaw(addresses, allowed, params), + simpleAllowListAbi, + simulateSimpleBudgetSetAuthorized, + ); + } + + public async setAuthorizedRaw( + addresses: Address[], + allowed: boolean[], + params: CallParams = {}, ) { return await writeSimpleBudgetSetAuthorized(this._config, { address: this.assertValidAddress(), @@ -161,8 +223,8 @@ export class SimpleBudget extends Deployable { } } return { - abi: SimpleBudgetArtifact.abi, - bytecode: SimpleBudgetArtifact.bytecode as Hex, + abi: simpleAllowListAbi, + bytecode: 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 abd826733..47a4a9549 100644 --- a/packages/sdk/src/Budgets/VestingBudget.ts +++ b/packages/sdk/src/Budgets/VestingBudget.ts @@ -8,14 +8,20 @@ import { readVestingBudgetEnd, readVestingBudgetIsAuthorized, readVestingBudgetTotal, + simulateVestingBudgetAllocate, + simulateVestingBudgetDisburse, + simulateVestingBudgetDisburseBatch, + simulateVestingBudgetReclaim, + simulateVestingBudgetSetAuthorized, + vestingBudgetAbi, writeVestingBudgetAllocate, writeVestingBudgetDisburse, writeVestingBudgetDisburseBatch, writeVestingBudgetReclaim, writeVestingBudgetSetAuthorized, } from '@boostxyz/evm'; -import VestingBudgetArtifact from '@boostxyz/evm/artifacts/contracts/budgets/VestingBudget.sol/VestingBudget.json'; -import { type Config, getAccount } from '@wagmi/core'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/budgets/VestingBudget.sol/VestingBudget.json'; +import { getAccount } from '@wagmi/core'; import { type Address, type Hex, zeroAddress } from 'viem'; import { Deployable, @@ -28,7 +34,18 @@ import type { CallParams } from '../utils'; export type { VestingBudgetPayload }; export class VestingBudget extends Deployable { - public allocate( + public async allocate( + transfer: TransferPayload, + params: CallParams = {}, + ) { + return this.awaitResult( + this.allocateRaw(transfer, params), + vestingBudgetAbi, + simulateVestingBudgetAllocate, + ); + } + + public allocateRaw( transfer: TransferPayload, params: CallParams = {}, ) { @@ -39,9 +56,18 @@ export class VestingBudget extends Deployable { }); } - // use prepareFungibleTransfer or prepareERC1155Transfer - // TODO use data structure - public reclaim( + public async reclaim( + transfer: TransferPayload, + params: CallParams = {}, + ) { + return this.awaitResult( + this.reclaimRaw(transfer, params), + vestingBudgetAbi, + simulateVestingBudgetReclaim, + ); + } + + public reclaimRaw( transfer: TransferPayload, params: CallParams = {}, ) { @@ -52,9 +78,18 @@ export class VestingBudget extends Deployable { }); } - // use prepareFungibleTransfer or prepareERC1155Transfer - // TODO use data structure - public disburse( + public async disburse( + transfer: TransferPayload, + params: CallParams = {}, + ) { + return this.awaitResult( + this.disburseRaw(transfer, params), + vestingBudgetAbi, + simulateVestingBudgetDisburse, + ); + } + + public disburseRaw( transfer: TransferPayload, params: CallParams = {}, ) { @@ -65,9 +100,18 @@ export class VestingBudget extends Deployable { }); } - // use prepareFungibleTransfer or prepareERC1155Transfer - // TODO use data structure - public disburseBatch( + public async disburseBatch( + transfers: TransferPayload[], + params: CallParams = {}, + ) { + return this.awaitResult( + this.disburseBatchRaw(transfers, params), + vestingBudgetAbi, + simulateVestingBudgetDisburseBatch, + ); + } + + public disburseBatchRaw( transfers: TransferPayload[], params: CallParams = {}, ) { @@ -82,6 +126,18 @@ export class VestingBudget extends Deployable { addresses: Address[], allowed: boolean[], params: CallParams = {}, + ) { + return this.awaitResult( + this.setAuthorizedRaw(addresses, allowed, params), + vestingBudgetAbi, + simulateVestingBudgetSetAuthorized, + ); + } + + public async setAuthorizedRaw( + addresses: Address[], + allowed: boolean[], + params: CallParams = {}, ) { return await writeVestingBudgetSetAuthorized(this._config, { address: this.assertValidAddress(), @@ -163,8 +219,8 @@ export class VestingBudget extends Deployable { } } return { - abi: VestingBudgetArtifact.abi, - bytecode: VestingBudgetArtifact.bytecode as Hex, + abi: vestingBudgetAbi, + bytecode: 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 9975994c6..da2c5bc71 100644 --- a/packages/sdk/src/Deployable/Contract.ts +++ b/packages/sdk/src/Deployable/Contract.ts @@ -1,5 +1,18 @@ -import type { Config } from '@wagmi/core'; -import type { Address } from 'viem'; +import { + type Config, + getTransaction, + waitForTransactionReceipt, +} from '@wagmi/core'; +import type { CreateSimulateContractReturnType } from '@wagmi/core/codegen'; +import { + type Abi, + type AbiStateMutability, + type Address, + type ContractFunctionArgs, + type ContractFunctionName, + type Hash, + decodeFunctionData, +} from 'viem'; import { ContractAddressRequiredError } from '../errors'; export class Contract { @@ -30,4 +43,31 @@ export class Contract { if (!address) throw new ContractAddressRequiredError(); return address; } + + protected async awaitResult< + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + >( + hashPromise: Promise, + abi: abi, + fn: CreateSimulateContractReturnType, + ) { + const hash = await hashPromise; + const receipt = await waitForTransactionReceipt(this._config, { + hash, + }); + const tx = await getTransaction(this._config, { hash }); + const { args } = decodeFunctionData({ + abi, + data: tx.input, + }); + const { result } = await fn(this._config, { + account: tx.from, + address: tx.to, + args: args as ContractFunctionArgs, + blockNumber: receipt.blockNumber, + // biome-ignore lint/suspicious/noExplicitAny: TODO this is an extremely complex type, but this method is intended for internal use and as such is alright for now + } as any); + return result; + } } diff --git a/packages/sdk/src/Incentives/AllowListIncentive.ts b/packages/sdk/src/Incentives/AllowListIncentive.ts index 162948632..c448f28e6 100644 --- a/packages/sdk/src/Incentives/AllowListIncentive.ts +++ b/packages/sdk/src/Incentives/AllowListIncentive.ts @@ -1,14 +1,16 @@ import { type AllowListIncentivePayload, type ClaimPayload, + allowListIncentiveAbi, prepareAllowListIncentivePayload, prepareClaimPayload, readAllowListIncentiveAllowList, readAllowListIncentiveIsClaimable, readAllowListIncentiveLimit, + simulateAllowListIncentiveClaim, writeAllowListIncentiveClaim, } from '@boostxyz/evm'; -import AllowListIncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/AllowListIncentive.sol/AllowListIncentive.json'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/incentives/AllowListIncentive.sol/AllowListIncentive.json'; import type { Hex } from 'viem'; import { SimpleAllowList } from '../AllowLists/AllowList'; import type { @@ -43,10 +45,20 @@ export class AllowListIncentive extends DeployableTarget = {}, + ) { + return this.awaitResult( + this.claimRaw(payload, params), + allowListIncentiveAbi, + simulateAllowListIncentiveClaim, + ); + } + + public async claimRaw( + payload: ClaimPayload, + params: CallParams = {}, ) { return writeAllowListIncentiveClaim(this._config, { address: this.assertValidAddress(), @@ -76,8 +88,8 @@ export class AllowListIncentive extends DeployableTarget { }); } - //prepareClaimPayload public async claim( payload: ClaimPayload, params: CallParams = {}, + ) { + return this.awaitResult( + this.claimRaw(payload, params), + cgdaIncentiveAbi, + simulateCgdaIncentiveClaim, + ); + } + + public async claimRaw( + payload: ClaimPayload, + params: CallParams = {}, ) { return writeCgdaIncentiveClaim(this._config, { address: this.assertValidAddress(), @@ -68,10 +81,20 @@ export class CGDAIncentive extends DeployableTarget { }); } - //prepareClaimPayload public async reclaim( payload: ClaimPayload, params: CallParams = {}, + ) { + return this.awaitResult( + this.reclaimRaw(payload, params), + cgdaIncentiveAbi, + simulateCgdaIncentiveReclaim, + ); + } + + public async reclaimRaw( + payload: ClaimPayload, + params: CallParams = {}, ) { return writeCgdaIncentiveReclaim(this._config, { address: this.assertValidAddress(), @@ -120,8 +143,8 @@ export class CGDAIncentive extends DeployableTarget { _options, ); return { - abi: CGDAIncentiveArtifact.abi, - bytecode: CGDAIncentiveArtifact.bytecode as Hex, + abi: cgdaIncentiveAbi, + bytecode: 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 550e0baec..d6ab3b3c3 100644 --- a/packages/sdk/src/Incentives/ERC1155Incentive.ts +++ b/packages/sdk/src/Incentives/ERC1155Incentive.ts @@ -2,6 +2,7 @@ import { type ClaimPayload, type ERC1155IncentivePayload, type StrategyType, + erc1155IncentiveAbi, prepareClaimPayload, prepareERC1155IncentivePayload, readErc1155IncentiveAsset, @@ -12,10 +13,12 @@ import { readErc1155IncentiveStrategy, readErc1155IncentiveTokenId, readErc1155SupportsInterface, + simulateErc1155IncentiveClaim, + simulateErc1155IncentiveReclaim, writeErc1155IncentiveClaim, writeErc1155IncentiveReclaim, } from '@boostxyz/evm'; -import ERC1155IncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/ERC1155Incentive.sol/ERC1155Incentive.json'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/incentives/ERC1155Incentive.sol/ERC1155Incentive.json'; import type { Hex } from 'viem'; import type { DeployableOptions, @@ -75,6 +78,17 @@ export class ERC1155Incentive extends DeployableTarget public async claim( payload: ClaimPayload, params: CallParams = {}, + ) { + return this.awaitResult( + this.claimRaw(payload, params), + erc1155IncentiveAbi, + simulateErc1155IncentiveClaim, + ); + } + + public async claimRaw( + payload: ClaimPayload, + params: CallParams = {}, ) { return writeErc1155IncentiveClaim(this._config, { address: this.assertValidAddress(), @@ -86,6 +100,17 @@ export class ERC1155Incentive extends DeployableTarget public async reclaim( payload: ClaimPayload, params: CallParams = {}, + ) { + return this.awaitResult( + this.reclaimRaw(payload, params), + erc1155IncentiveAbi, + simulateErc1155IncentiveReclaim, + ); + } + + public async reclaimRaw( + payload: ClaimPayload, + params: CallParams = {}, ) { return writeErc1155IncentiveReclaim(this._config, { address: this.assertValidAddress(), @@ -136,8 +161,8 @@ export class ERC1155Incentive extends DeployableTarget _options, ); return { - abi: ERC1155IncentiveArtifact.abi, - bytecode: ERC1155IncentiveArtifact.bytecode as Hex, + abi: erc1155IncentiveAbi, + bytecode: 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 7bc017032..a5f8f138b 100644 --- a/packages/sdk/src/Incentives/ERC20Incentive.ts +++ b/packages/sdk/src/Incentives/ERC20Incentive.ts @@ -2,6 +2,7 @@ import { type ClaimPayload, type ERC20IncentivePayload, type StrategyType, + erc20IncentiveAbi, prepareClaimPayload, prepareERC20IncentivePayload, readErc20IncentiveAsset, @@ -11,11 +12,14 @@ import { readErc20IncentivePreflight, readErc20IncentiveReward, readErc20IncentiveStrategy, + simulateErc20IncentiveClaim, + simulateErc20IncentiveDrawRaffle, + simulateErc20IncentiveReclaim, writeErc20IncentiveClaim, writeErc20IncentiveDrawRaffle, writeErc20IncentiveReclaim, } from '@boostxyz/evm'; -import ERC20IncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/ERC20Incentive.sol/ERC20Incentive.json'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/incentives/ERC20Incentive.sol/ERC20Incentive.json'; import type { Hex } from 'viem'; import { Deployable, @@ -74,6 +78,17 @@ export class ERC20Incentive extends DeployableTarget { public async claim( payload: ClaimPayload, params: CallParams = {}, + ) { + return this.awaitResult( + this.claimRaw(payload, params), + erc20IncentiveAbi, + simulateErc20IncentiveClaim, + ); + } + + public async claimRaw( + payload: ClaimPayload, + params: CallParams = {}, ) { return writeErc20IncentiveClaim(this._config, { address: this.assertValidAddress(), @@ -82,10 +97,20 @@ export class ERC20Incentive extends DeployableTarget { }); } - //prepareClaimPayload public async reclaim( payload: ClaimPayload, params: CallParams = {}, + ) { + return this.awaitResult( + this.reclaimRaw(payload, params), + erc20IncentiveAbi, + simulateErc20IncentiveReclaim, + ); + } + + public async reclaimRaw( + payload: ClaimPayload, + params: CallParams = {}, ) { return writeErc20IncentiveReclaim(this._config, { address: this.assertValidAddress(), @@ -94,7 +119,6 @@ export class ERC20Incentive extends DeployableTarget { }); } - //prepareClaimPayload public async isClaimable( payload: ClaimPayload, params: CallParams = {}, @@ -119,6 +143,16 @@ export class ERC20Incentive extends DeployableTarget { public async drawRaffle( params: CallParams = {}, + ) { + return this.awaitResult( + this.drawRaffleRaw(params), + erc20IncentiveAbi, + simulateErc20IncentiveDrawRaffle, + ); + } + + public async drawRaffleRaw( + params: CallParams = {}, ) { return writeErc20IncentiveDrawRaffle(this._config, { address: this.assertValidAddress(), @@ -135,8 +169,8 @@ export class ERC20Incentive extends DeployableTarget { _options, ); return { - abi: ERC20IncentiveArtifact.abi, - bytecode: ERC20IncentiveArtifact.bytecode as Hex, + abi: erc20IncentiveAbi, + bytecode: 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 ccb43c16f..20d9a59b6 100644 --- a/packages/sdk/src/Incentives/PointsIncentive.ts +++ b/packages/sdk/src/Incentives/PointsIncentive.ts @@ -1,6 +1,7 @@ import { type ClaimPayload, type PointsIncentivePayload, + pointsIncentiveAbi, prepareClaimPayload, preparePointsIncentivePayload, readPointsIncentiveIsClaimable, @@ -8,9 +9,10 @@ import { readPointsIncentiveQuantity, readPointsIncentiveSelector, readPointsIncentiveVenue, + simulatePointsIncentiveClaim, writePointsIncentiveClaim, } from '@boostxyz/evm'; -import PointsIncentiveArtifact from '@boostxyz/evm/artifacts/contracts/incentives/PointsIncentive.sol/PointsIncentive.json'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/incentives/PointsIncentive.sol/PointsIncentive.json'; import type { Hex } from 'viem'; import type { DeployableOptions, @@ -54,10 +56,20 @@ export class PointsIncentive extends DeployableTarget { }); } - //prepareClaimPayload public async claim( payload: ClaimPayload, params: CallParams = {}, + ) { + return this.awaitResult( + this.claimRaw(payload, params), + pointsIncentiveAbi, + simulatePointsIncentiveClaim, + ); + } + + public async claimRaw( + payload: ClaimPayload, + params: CallParams = {}, ) { return writePointsIncentiveClaim(this._config, { address: this.assertValidAddress(), @@ -87,8 +99,8 @@ export class PointsIncentive extends DeployableTarget { _options, ); return { - abi: PointsIncentiveArtifact.abi, - bytecode: PointsIncentiveArtifact.bytecode as Hex, + abi: pointsIncentiveAbi, + bytecode: 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 76ba1f590..02dc17c1f 100644 --- a/packages/sdk/src/Validators/SignerValidator.ts +++ b/packages/sdk/src/Validators/SignerValidator.ts @@ -4,10 +4,13 @@ import { prepareSignerValidatorPayload, prepareSignerValidatorValidatePayload, readSignerValidatorSigners, + signerValidatorAbi, + simulateSignerValidatorSetAuthorized, + simulateSignerValidatorValidate, writeSignerValidatorSetAuthorized, writeSignerValidatorValidate, } from '@boostxyz/evm'; -import SignerValidatorArtifact from '@boostxyz/evm/artifacts/contracts/validators/SignerValidator.sol/SignerValidator.json'; +import { bytecode } from '@boostxyz/evm/artifacts/contracts/validators/SignerValidator.sol/SignerValidator.json'; import type { Address, Hex } from 'viem'; import type { DeployableOptions, @@ -33,6 +36,17 @@ export class SignerValidator extends DeployableTarget { public async validate( payload: SignerValidatorValidatePayload, params: CallParams = {}, + ) { + return this.awaitResult( + this.validateRaw(payload, params), + signerValidatorAbi, + simulateSignerValidatorValidate, + ); + } + + public async validateRaw( + payload: SignerValidatorValidatePayload, + params: CallParams = {}, ) { return writeSignerValidatorValidate(this._config, { address: this.assertValidAddress(), @@ -45,6 +59,18 @@ export class SignerValidator extends DeployableTarget { addresses: Address[], allowed: boolean[], params: CallParams = {}, + ) { + return this.awaitResult( + this.setAuthorizedRaw(addresses, allowed, params), + signerValidatorAbi, + simulateSignerValidatorSetAuthorized, + ); + } + + public async setAuthorizedRaw( + addresses: Address[], + allowed: boolean[], + params: CallParams = {}, ) { return await writeSignerValidatorSetAuthorized(this._config, { address: this.assertValidAddress(), @@ -62,8 +88,8 @@ export class SignerValidator extends DeployableTarget { _options, ); return { - abi: SignerValidatorArtifact.abi, - bytecode: SignerValidatorArtifact.bytecode as Hex, + abi: signerValidatorAbi, + bytecode: 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 fe80af532..4b5b3e6fd 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -23,6 +23,8 @@ export * from './Budgets/VestingBudget'; // Deployable export * from './Deployable/Deployable'; +export * from './Deployable/Contract'; +export * from './Deployable/DeployableTarget'; // Incentives diff --git a/packages/sdk/test/helpers.ts b/packages/sdk/test/helpers.ts index 99594ad80..5545d28ee 100644 --- a/packages/sdk/test/helpers.ts +++ b/packages/sdk/test/helpers.ts @@ -1,5 +1,4 @@ -import { RegistryType } from '@boostxyz/evm'; -import { writeBoostRegistryRegister } from '@boostxyz/evm'; +import { RegistryType, writeBoostRegistryRegister } from '@boostxyz/evm'; import BoostCore from '@boostxyz/evm/artifacts/contracts/BoostCore.sol/BoostCore.json'; import BoostRegistry from '@boostxyz/evm/artifacts/contracts/BoostRegistry.sol/BoostRegistry.json'; import ContractAction from '@boostxyz/evm/artifacts/contracts/actions/ContractAction.sol/ContractAction.json'; @@ -15,12 +14,16 @@ import ERC1155Incentive from '@boostxyz/evm/artifacts/contracts/incentives/ERC11 import PointsIncentive from '@boostxyz/evm/artifacts/contracts/incentives/PointsIncentive.sol/PointsIncentive.json'; import SignerValidator from '@boostxyz/evm/artifacts/contracts/validators/SignerValidator.sol/SignerValidator.json'; import type { Address, Hex } from 'viem'; -import { accounts } from './accounts'; -import { mockWalletClient, setupConfig, testAccount } from './viem'; +import { + type TestClient, + makeTestClient, + setupConfig, + testAccount, +} from './viem'; export async function deployContract( - client: typeof mockWalletClient, - ...params: Parameters + client: TestClient, + ...params: Parameters ) { const tx = await client.waitForTransactionReceipt({ hash: await client.deployContract(...params), @@ -31,8 +34,8 @@ export async function deployContract( export type Fixtures = Awaited>; export async function deployFixtures( - account: Address = accounts.at(0)?.account, - client = mockWalletClient, + account = testAccount, + client = makeTestClient(), ) { const registry = await deployContract(client, { abi: BoostRegistry.abi, @@ -44,7 +47,7 @@ export async function deployFixtures( abi: BoostCore.abi, bytecode: BoostCore.bytecode as Hex, account, - args: [registry, account], + args: [registry, account.address], }); const bases = { diff --git a/packages/sdk/test/viem.ts b/packages/sdk/test/viem.ts index 86550f059..32dbd0149 100644 --- a/packages/sdk/test/viem.ts +++ b/packages/sdk/test/viem.ts @@ -19,17 +19,20 @@ const { account, key } = accounts.at(0) || { export { account, key }; export const testAccount = privateKeyToAccount(key); -export const mockWalletClient = createTestClient({ - transport: http(), - chain: hardhat, - mode: 'hardhat', - account, - key, -}) - .extend(publicActions) - .extend(walletActions); +export const makeTestClient = () => + createTestClient({ + transport: http(), + chain: hardhat, + mode: 'hardhat', + account: testAccount, + key, + }) + .extend(publicActions) + .extend(walletActions); -export function setupConfig(walletClient = mockWalletClient) { +export type TestClient = ReturnType; + +export function setupConfig(walletClient = makeTestClient()) { return createConfig({ chains: [hardhat], client: () => walletClient,