Skip to content

Commit

Permalink
feat: Choose constructor method in Contract.deploy (#4939)
Browse files Browse the repository at this point in the history
Adds a new `deployWithOpts` method to the autogenerated contract
typescript interfaces which allows the caller to choose which
constructor method to call during deployment. Arguments are type-checked
based on the method chosen.

Depends on #4896
  • Loading branch information
spalladino committed Mar 5, 2024
1 parent e96aaaa commit e899e56
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 10 deletions.
16 changes: 12 additions & 4 deletions yarn-project/aztec.js/src/contract/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ export class Contract extends ContractBase {
* @param wallet - The wallet for executing the deployment.
* @param artifact - Build artifact of the contract to deploy
* @param args - Arguments for the constructor.
* @param constructorName - The name of the constructor function to call.
*/
public static deploy(wallet: Wallet, artifact: ContractArtifact, args: any[]) {
public static deploy(wallet: Wallet, artifact: ContractArtifact, args: any[], constructorName?: string) {
const postDeployCtor = (address: AztecAddress, wallet: Wallet) => Contract.at(address, artifact, wallet);
return new DeployMethod(Point.ZERO, wallet, artifact, postDeployCtor, args);
return new DeployMethod(Point.ZERO, wallet, artifact, postDeployCtor, args, constructorName);
}

/**
Expand All @@ -48,9 +49,16 @@ export class Contract extends ContractBase {
* @param wallet - The wallet for executing the deployment.
* @param artifact - Build artifact of the contract.
* @param args - Arguments for the constructor.
* @param constructorName - The name of the constructor function to call.
*/
public static deployWithPublicKey(publicKey: PublicKey, wallet: Wallet, artifact: ContractArtifact, args: any[]) {
public static deployWithPublicKey(
publicKey: PublicKey,
wallet: Wallet,
artifact: ContractArtifact,
args: any[],
constructorName?: string,
) {
const postDeployCtor = (address: AztecAddress, wallet: Wallet) => Contract.at(address, artifact, wallet);
return new DeployMethod(publicKey, wallet, artifact, postDeployCtor, args);
return new DeployMethod(publicKey, wallet, artifact, postDeployCtor, args, constructorName);
}
}
3 changes: 2 additions & 1 deletion yarn-project/aztec.js/src/contract/deploy_method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
private artifact: ContractArtifact,
private postDeployCtor: (address: AztecAddress, wallet: Wallet) => Promise<TContract>,
private args: any[] = [],
constructorName: string = 'constructor',
) {
super(wallet);
const constructorArtifact = artifact.functions.find(f => f.name === 'constructor');
const constructorArtifact = artifact.functions.find(f => f.name === constructorName);
if (!constructorArtifact) {
throw new Error('Cannot find constructor in the artifact.');
}
Expand Down
16 changes: 14 additions & 2 deletions yarn-project/aztec.js/src/deployment/contract_deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import { Contract } from '../contract/index.js';
* @remarks Keeping this around even though we have Aztec.nr contract types because it can be useful for non-TS users.
*/
export class ContractDeployer {
constructor(private artifact: ContractArtifact, private wallet: Wallet, private publicKey?: PublicKey) {}
constructor(
private artifact: ContractArtifact,
private wallet: Wallet,
private publicKey?: PublicKey,
private constructorName?: string,
) {}

/**
* Deploy a contract using the provided ABI and constructor arguments.
Expand All @@ -25,6 +30,13 @@ export class ContractDeployer {
*/
public deploy(...args: any[]) {
const postDeployCtor = (address: AztecAddress, wallet: Wallet) => Contract.at(address, this.artifact, wallet);
return new DeployMethod(this.publicKey ?? Point.ZERO, this.wallet, this.artifact, postDeployCtor, args);
return new DeployMethod(
this.publicKey ?? Point.ZERO,
this.wallet,
this.artifact,
postDeployCtor,
args,
this.constructorName,
);
}
}
12 changes: 12 additions & 0 deletions yarn-project/end-to-end/src/e2e_deploy_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,18 @@ describe('e2e_deploy_contract', () => {
expect(await token.methods.is_minter(owner).view()).toEqual(true);
}, 60_000);

it('publicly deploys and initializes via a public function', async () => {
const owner = accounts[0];
logger.debug(`Deploying contract via a public constructor`);
const contract = await StatefulTestContract.deployWithOpts({ wallet, method: 'public_constructor' }, owner, 42)
.send()
.deployed();
expect(await contract.methods.get_public_value(owner).view()).toEqual(42n);
logger.debug(`Calling a private function to ensure the contract was properly initialized`);
await contract.methods.create_note(owner, 30).send().wait();
expect(await contract.methods.summed_values(owner).view()).toEqual(30n);
}, 60_000);

it.skip('publicly deploys and calls a public function in the same batched call', async () => {
// TODO(@spalladino)
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,31 @@ function generateDeploy(input: ContractArtifact) {
* Creates a tx to deploy a new instance of this contract.
*/
public static deploy(wallet: Wallet, ${args}) {
return new DeployMethod<${input.name}Contract>(Point.ZERO, wallet, ${artifactName}, ${contractName}.at, Array.from(arguments).slice(1));
return new DeployMethod<${contractName}>(Point.ZERO, wallet, ${artifactName}, ${contractName}.at, Array.from(arguments).slice(1));
}
/**
* Creates a tx to deploy a new instance of this contract using the specified public key to derive the address.
*/
public static deployWithPublicKey(publicKey: PublicKey, wallet: Wallet, ${args}) {
return new DeployMethod<${input.name}Contract>(publicKey, wallet, ${artifactName}, ${contractName}.at, Array.from(arguments).slice(2));
return new DeployMethod<${contractName}>(publicKey, wallet, ${artifactName}, ${contractName}.at, Array.from(arguments).slice(2));
}
/**
* Creates a tx to deploy a new instance of this contract using the specified constructor method.
*/
public static deployWithOpts<M extends keyof ${contractName}['methods']>(
opts: { publicKey?: PublicKey; method?: M; wallet: Wallet },
...args: Parameters<${contractName}['methods'][M]>
) {
return new DeployMethod<${contractName}>(
opts.publicKey ?? Point.ZERO,
opts.wallet,
${artifactName},
${contractName}.at,
Array.from(arguments).slice(1),
opts.method ?? 'constructor',
);
}
`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ export class ClientExecutionContext extends ViewDataOracle {
// side-effects occurred in the TX. Ultimately the private kernel should
// just output everything in the proper order without any counters.
this.log(
`Enqueued call to public function (with side-effect counter #${sideEffectCounter}) ${targetContractAddress}:${functionSelector}`,
`Enqueued call to public function (with side-effect counter #${sideEffectCounter}) ${targetContractAddress}:${functionSelector}(${targetArtifact.name})`,
);

this.enqueuedPublicFunctionCalls.push(enqueuedRequest);
Expand Down

0 comments on commit e899e56

Please sign in to comment.