Skip to content

Commit

Permalink
chore: add account to configuration to make some contract ops easier
Browse files Browse the repository at this point in the history
  • Loading branch information
sammccord committed Jun 26, 2024
1 parent 11bd82c commit b478220
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 158 deletions.
293 changes: 147 additions & 146 deletions packages/sdk/src/BoostClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Config, getAccount } from '@wagmi/core';
import { getAccount } from '@wagmi/core';
import { createWriteContract } from '@wagmi/core/codegen';
import { type Address, zeroAddress, zeroHash } from 'viem';
import {
Expand Down Expand Up @@ -29,6 +29,7 @@ import {
VestingBudget,
type VestingBudgetPayload,
} from './Budgets/VestingBudget';
import type { Config } from './Deployable/Contract';
import type { Deployable, DeployableOptions } from './Deployable/Deployable';
import {
AllowListIncentive,
Expand Down Expand Up @@ -84,151 +85,151 @@ export class BoostClient {
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<BoostPayload, 'address'>): 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<OnChainBoostPayload, 'budget'> = {
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<OnChainBoostPayload, 'action'> = {
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<OnChainBoostPayload, 'validator'> = {
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<OnChainBoostPayload, 'allowList'> = {
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<OnChainBoostPayload, 'incentives'> = {
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?
// // TODO revisit this, necessary?
// public async *createBoostWithProgress({
// budget,
// action,
// validator,
// allowList,
// incentives,
// protocolFee = 0n,
// referralFee = 0n,
// maxParticipants = 0n,
// owner = zeroAddress,
// }: Omit<BoostPayload, 'address'>): 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<OnChainBoostPayload, 'budget'> = {
// 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<OnChainBoostPayload, 'action'> = {
// 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<OnChainBoostPayload, 'validator'> = {
// 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<OnChainBoostPayload, 'allowList'> = {
// 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<OnChainBoostPayload, 'incentives'> = {
// 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({
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/Deployable/Contract.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Config } from '@wagmi/core';
import type { Address } from 'viem';
import type { Account, Address } from 'viem';
import { ContractAddressRequiredError } from '../errors';

export class Contract {
Expand Down
41 changes: 30 additions & 11 deletions packages/sdk/src/Deployable/Deployable.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { type Config, deployContract } from '@wagmi/core';
import type { Address, Hex } from 'viem';
import {
type Config as WagmiConfig,
deployContract,
getClient,
} from '@wagmi/core';
import type { Account, Address, Hash, Hex } from 'viem';
import {
DeployableAlreadyDeployedError,
DeployableBuildParametersUnspecifiedError,
Expand All @@ -17,16 +21,24 @@ export type GenericDeployableParams = Omit<

export type DeployableOptions<Payload = unknown> = Payload | Address;

export interface DeployableConfig extends WagmiConfig {
account?: Account;
}
export class Deployable<Payload = unknown> extends Contract {
protected _payload: Payload | undefined;
protected _account?: Account;

constructor(config: Config, options: DeployableOptions<Payload>) {
constructor(
{ account, ...config }: DeployableConfig,
options: DeployableOptions<Payload>,
) {
if (typeof options === 'string') {
super(config, options as Address);
} else {
super(config, undefined);
this._payload = options as Payload;
}
if (account) this._account = account;
}

get payload() {
Expand All @@ -38,28 +50,35 @@ export class Deployable<Payload = unknown> extends Contract {
return this;
}

public async deploy(_payload?: Payload, _config?: Config): Promise<Address> {
public async deploy(
_payload?: Payload,
_config?: DeployableConfig,
): Promise<Hash> {
if (this.address) throw new DeployableAlreadyDeployedError(this.address);
const config = _config || this._config;
const payload = _payload || this._payload;
return (this._address = await deployContract(
config,
this.buildParameters(payload),
));
const account = _config?.account || this._account;
return await deployContract(config, {
...this.buildParameters(payload),
...(account ? { account } : {}),
});
}

public buildParameters(
_payload?: Payload,
_config?: Config,
_config?: DeployableConfig,
): GenericDeployableParams {
throw new DeployableBuildParametersUnspecifiedError();
}

public validateDeploymentConfig(_payload?: Payload, _config?: Config) {
public validateDeploymentConfig(
_payload?: Payload,
_config?: DeployableConfig,
) {
const config = _config || this._config;
if (!config) throw new DeployableWagmiConfigurationRequiredError();
const payload = _payload || this._payload;
if (!payload) throw new DeployableMissingPayloadError();
return [payload, config] as [Payload, Config];
return [payload, config] as [Payload, WagmiConfig];
}
}

0 comments on commit b478220

Please sign in to comment.