@aztec/aztec.js 0.1.0-alpha13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/.tsbuildinfo +1 -0
  3. package/README.md +44 -0
  4. package/dest/account_impl/account_collection.d.ts +24 -0
  5. package/dest/account_impl/account_collection.d.ts.map +1 -0
  6. package/dest/account_impl/account_collection.js +36 -0
  7. package/dest/account_impl/account_contract.d.ts +45 -0
  8. package/dest/account_impl/account_contract.d.ts.map +1 -0
  9. package/dest/account_impl/account_contract.js +108 -0
  10. package/dest/account_impl/index.d.ts +11 -0
  11. package/dest/account_impl/index.d.ts.map +1 -0
  12. package/dest/account_impl/index.js +3 -0
  13. package/dest/auth/ecdsa.d.ts +13 -0
  14. package/dest/auth/ecdsa.d.ts.map +1 -0
  15. package/dest/auth/ecdsa.js +17 -0
  16. package/dest/auth/index.d.ts +25 -0
  17. package/dest/auth/index.d.ts.map +1 -0
  18. package/dest/auth/index.js +13 -0
  19. package/dest/aztec_rpc_client/aztec_rpc_client.d.ts +25 -0
  20. package/dest/aztec_rpc_client/aztec_rpc_client.d.ts.map +1 -0
  21. package/dest/aztec_rpc_client/aztec_rpc_client.js +20 -0
  22. package/dest/aztec_rpc_client/index.d.ts +3 -0
  23. package/dest/aztec_rpc_client/index.d.ts.map +1 -0
  24. package/dest/aztec_rpc_client/index.js +3 -0
  25. package/dest/aztec_rpc_client/wallet.d.ts +44 -0
  26. package/dest/aztec_rpc_client/wallet.d.ts.map +1 -0
  27. package/dest/aztec_rpc_client/wallet.js +69 -0
  28. package/dest/contract/checker.d.ts +11 -0
  29. package/dest/contract/checker.d.ts.map +1 -0
  30. package/dest/contract/checker.js +98 -0
  31. package/dest/contract/checker.test.d.ts +2 -0
  32. package/dest/contract/checker.test.d.ts.map +1 -0
  33. package/dest/contract/checker.test.js +168 -0
  34. package/dest/contract/contract.d.ts +69 -0
  35. package/dest/contract/contract.d.ts.map +1 -0
  36. package/dest/contract/contract.js +64 -0
  37. package/dest/contract/contract.test.d.ts +2 -0
  38. package/dest/contract/contract.test.d.ts.map +1 -0
  39. package/dest/contract/contract.test.js +141 -0
  40. package/dest/contract/contract_function_interaction.d.ts +81 -0
  41. package/dest/contract/contract_function_interaction.d.ts.map +1 -0
  42. package/dest/contract/contract_function_interaction.js +96 -0
  43. package/dest/contract/index.d.ts +4 -0
  44. package/dest/contract/index.d.ts.map +1 -0
  45. package/dest/contract/index.js +4 -0
  46. package/dest/contract/sent_tx.d.ts +36 -0
  47. package/dest/contract/sent_tx.d.ts.map +1 -0
  48. package/dest/contract/sent_tx.js +50 -0
  49. package/dest/contract_deployer/contract_deployer.d.ts +23 -0
  50. package/dest/contract_deployer/contract_deployer.d.ts.map +1 -0
  51. package/dest/contract_deployer/contract_deployer.js +25 -0
  52. package/dest/contract_deployer/contract_deployer.test.d.ts +2 -0
  53. package/dest/contract_deployer/contract_deployer.test.d.ts.map +1 -0
  54. package/dest/contract_deployer/contract_deployer.test.js +48 -0
  55. package/dest/contract_deployer/deploy_method.d.ts +72 -0
  56. package/dest/contract_deployer/deploy_method.d.ts.map +1 -0
  57. package/dest/contract_deployer/deploy_method.js +114 -0
  58. package/dest/contract_deployer/index.d.ts +2 -0
  59. package/dest/contract_deployer/index.d.ts.map +1 -0
  60. package/dest/contract_deployer/index.js +2 -0
  61. package/dest/index.d.ts +8 -0
  62. package/dest/index.d.ts.map +1 -0
  63. package/dest/index.js +9 -0
  64. package/dest/utils/account.d.ts +9 -0
  65. package/dest/utils/account.d.ts.map +1 -0
  66. package/dest/utils/account.js +38 -0
  67. package/dest/utils/index.d.ts +4 -0
  68. package/dest/utils/index.d.ts.map +1 -0
  69. package/dest/utils/index.js +4 -0
  70. package/dest/utils/pub_key.d.ts +18 -0
  71. package/dest/utils/pub_key.d.ts.map +1 -0
  72. package/dest/utils/pub_key.js +25 -0
  73. package/dest/utils/secrets.d.ts +8 -0
  74. package/dest/utils/secrets.d.ts.map +1 -0
  75. package/dest/utils/secrets.js +12 -0
  76. package/package.json +14 -0
  77. package/src/account_impl/account_collection.ts +41 -0
  78. package/src/account_impl/account_contract.ts +173 -0
  79. package/src/account_impl/index.ts +12 -0
  80. package/src/auth/ecdsa.ts +18 -0
  81. package/src/auth/index.ts +32 -0
  82. package/src/aztec_rpc_client/aztec_rpc_client.ts +59 -0
  83. package/src/aztec_rpc_client/index.ts +2 -0
  84. package/src/aztec_rpc_client/wallet.ts +87 -0
  85. package/src/contract/checker.test.ts +177 -0
  86. package/src/contract/checker.ts +117 -0
  87. package/src/contract/contract.test.ts +160 -0
  88. package/src/contract/contract.ts +80 -0
  89. package/src/contract/contract_function_interaction.ts +141 -0
  90. package/src/contract/index.ts +3 -0
  91. package/src/contract/sent_tx.ts +55 -0
  92. package/src/contract_deployer/contract_deployer.test.ts +56 -0
  93. package/src/contract_deployer/contract_deployer.ts +24 -0
  94. package/src/contract_deployer/deploy_method.ts +165 -0
  95. package/src/contract_deployer/index.ts +1 -0
  96. package/src/index.ts +9 -0
  97. package/src/utils/account.ts +59 -0
  98. package/src/utils/index.ts +3 -0
  99. package/src/utils/pub_key.ts +26 -0
  100. package/src/utils/secrets.ts +12 -0
  101. package/tsconfig.json +26 -0
@@ -0,0 +1,177 @@
1
+ import { abiChecker } from './checker.js';
2
+
3
+ describe('abiChecker', () => {
4
+ let abi: any;
5
+
6
+ it('should throw an error if it has no functions', () => {
7
+ abi = {
8
+ name: 'TEST_ABI',
9
+ };
10
+ expect(() => abiChecker(abi)).toThrowError('ABI has no functions');
11
+ abi = {
12
+ name: 'TEST_ABI',
13
+ functions: [],
14
+ };
15
+ expect(() => abiChecker(abi)).toThrowError('ABI has no functions');
16
+ });
17
+
18
+ it('should error if ABI has no names', () => {
19
+ abi = {
20
+ name: 'TEST_ABI',
21
+ functions: [{ bytecode: '0af', parameters: [{ type: { kind: 'test' } }] }],
22
+ };
23
+ expect(() => abiChecker(abi)).toThrowError('ABI function has no name');
24
+ });
25
+
26
+ it('should error if ABI function has unrecognised type', () => {
27
+ abi = {
28
+ name: 'TEST_ABI',
29
+ functions: [
30
+ {
31
+ name: 'Function name',
32
+ bytecode: '0af',
33
+ parameters: [{ type: { kind: 'test' } }],
34
+ },
35
+ ],
36
+ };
37
+ expect(() => abiChecker(abi)).toThrowError('ABI function parameter has an unrecognised type');
38
+ });
39
+
40
+ it('should error if integer is incorrectly formed', () => {
41
+ abi = {
42
+ name: 'TEST_ABI',
43
+ functions: [
44
+ {
45
+ name: 'constructor',
46
+ bytecode: '0af',
47
+ parameters: [{ type: { kind: 'integer', sign: 5 } }],
48
+ },
49
+ ],
50
+ };
51
+ expect(() => abiChecker(abi)).toThrowError('Unrecognised attribute on type integer');
52
+ });
53
+
54
+ it('should error if string is incorrectly formed', () => {
55
+ abi = {
56
+ name: 'TEST_ABI',
57
+ functions: [
58
+ {
59
+ name: 'constructor',
60
+ bytecode: '0af',
61
+ parameters: [{ type: { kind: 'string', sign: 5, additionalParam: true } }],
62
+ },
63
+ ],
64
+ };
65
+ expect(() => abiChecker(abi)).toThrowError('Unrecognised attribute on type string');
66
+ });
67
+
68
+ it('should error if struct is incorrectly formed', () => {
69
+ abi = {
70
+ name: 'TEST_ABI',
71
+ functions: [
72
+ {
73
+ name: 'constructor',
74
+ bytecode: '0af',
75
+ parameters: [
76
+ {
77
+ type: {
78
+ kind: 'struct',
79
+ },
80
+ },
81
+ ],
82
+ },
83
+ ],
84
+ };
85
+ expect(() => abiChecker(abi)).toThrowError('Unrecognised attribute on type struct');
86
+ });
87
+
88
+ it('should error if array is incorrectly formed', () => {
89
+ abi = {
90
+ name: 'TEST_ABI',
91
+ functions: [
92
+ {
93
+ name: 'constructor',
94
+ bytecode: '0af',
95
+ parameters: [
96
+ {
97
+ type: {
98
+ kind: 'array',
99
+ length: 5,
100
+ type: {
101
+ kind: 'array',
102
+ length: '5',
103
+ type: {
104
+ sign: 'value',
105
+ width: 5,
106
+ kind: 'integer',
107
+ },
108
+ },
109
+ },
110
+ },
111
+ ],
112
+ },
113
+ ],
114
+ };
115
+ expect(() => abiChecker(abi)).toThrowError('ABI function parameter has an incorrectly formed array');
116
+ });
117
+
118
+ it('valid matrix should pass checker', () => {
119
+ abi = {
120
+ name: 'TEST_ABI',
121
+ functions: [
122
+ {
123
+ name: 'constructor',
124
+ bytecode: '0af',
125
+ parameters: [
126
+ {
127
+ type: {
128
+ kind: 'array',
129
+ length: 5,
130
+ type: {
131
+ kind: 'array',
132
+ length: 5,
133
+ type: {
134
+ sign: 'value',
135
+ width: 5,
136
+ kind: 'integer',
137
+ },
138
+ },
139
+ },
140
+ },
141
+ ],
142
+ },
143
+ ],
144
+ };
145
+ expect(abiChecker(abi)).toBe(true);
146
+ });
147
+
148
+ it('valid struct should pass checker', () => {
149
+ abi = {
150
+ name: 'TEST_ABI',
151
+ functions: [
152
+ {
153
+ name: 'constructor',
154
+ bytecode: '0af',
155
+ parameters: [
156
+ {
157
+ type: {
158
+ kind: 'struct',
159
+ fields: [
160
+ {
161
+ name: 'name',
162
+ type: {
163
+ sign: 'value',
164
+ width: 5,
165
+ kind: 'integer',
166
+ },
167
+ },
168
+ ],
169
+ },
170
+ },
171
+ ],
172
+ },
173
+ ],
174
+ };
175
+ expect(abiChecker(abi)).toBe(true);
176
+ });
177
+ });
@@ -0,0 +1,117 @@
1
+ import { ABIType, BasicType, ContractAbi, StructType } from '@aztec/foundation/abi';
2
+
3
+ /**
4
+ * Represents a type derived from input type T with the 'kind' property removed.
5
+ * Useful when checking attributes of a specific kind and validating their types.
6
+ */
7
+ type TypeWithoutKind<T> = Omit<{ [key in keyof T]: any }, 'kind'>;
8
+
9
+ /**
10
+ * Validates the given ContractAbi object by checking its functions and their parameters.
11
+ * Ensures that the ABI has at least one function, a constructor, valid bytecode, and correct parameter types.
12
+ * Throws an error if any inconsistency is detected during the validation process.
13
+ *
14
+ * @param abi - The ContractAbi object to be validated.
15
+ * @returns A boolean value indicating whether the ABI is valid or not.
16
+ */
17
+ export function abiChecker(abi: ContractAbi) {
18
+ if (!abi.functions || abi.functions.length === 0) {
19
+ throw new Error('ABI has no functions');
20
+ }
21
+
22
+ abi.functions.forEach(func => {
23
+ if (!('name' in func && typeof func.name === 'string' && func.name.length > 0)) {
24
+ throw new Error('ABI function has no name');
25
+ }
26
+
27
+ // TODO: implement a better check for bytecode (right now only checks if it's > 0)
28
+ if (!('bytecode' in func && typeof func.bytecode === 'string' && func.bytecode.length > 0)) {
29
+ throw new Error('ABI function parameter has incorrect bytecode');
30
+ }
31
+
32
+ func.parameters.forEach(param => {
33
+ if (!param.type) {
34
+ throw new Error('ABI function parameter has no type');
35
+ }
36
+
37
+ abiParameterTypeChecker(param.type);
38
+ });
39
+ });
40
+
41
+ // TODO: implement a better check for constructor (right now only checks if it has it or not)
42
+ if (!abi.functions.find(func => func.name === 'constructor')) {
43
+ throw new Error('ABI has no constructor');
44
+ }
45
+
46
+ return true;
47
+ }
48
+
49
+ /**
50
+ * Validates the ABI function parameter's type by checking its kind and attributes.
51
+ * Throws an error if the type has an unrecognized kind or incorrectly formed attributes.
52
+ * Additionally, checks nested types for array and struct kinds.
53
+ *
54
+ * @param type - The ABIType object representing the type of the ABI function parameter.
55
+ * @returns A boolean value indicating whether the type is valid or not.
56
+ */
57
+ function abiParameterTypeChecker(type: ABIType): boolean {
58
+ switch (type.kind) {
59
+ case 'field':
60
+ case 'boolean':
61
+ return checkAttributes(type, {});
62
+ case 'integer':
63
+ return checkAttributes(type, { sign: 'string', width: 'number' });
64
+ case 'string':
65
+ return checkAttributes(type, { length: 'number' });
66
+ case 'array':
67
+ return checkAttributes(type, { length: 'number', type: 'object' }) && abiParameterTypeChecker(type.type);
68
+ case 'struct':
69
+ return checkAttributes(type, { fields: 'object' }) && checkStruct(type);
70
+ default:
71
+ throw new Error('ABI function parameter has an unrecognised type');
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Check if the structure of the ABIType 'struct' is valid by ensuring field names are strings
77
+ * and their type attribute passes the abiParameterTypeChecker. Returns true on successful validation,
78
+ * otherwise throws an error providing insight into the incorrect formation in the struct.
79
+ *
80
+ * @param type - The StructType object containing an array of fields to validate.
81
+ * @returns A boolean value indicating successful validation of the struct's fields.
82
+ */
83
+ function checkStruct(type: StructType) {
84
+ return type.fields.reduce((acc, field) => {
85
+ if (!('name' in field && typeof field.name === 'string')) {
86
+ throw new Error('ABI function parameter has an incorrectly formed struct');
87
+ }
88
+ return acc && abiParameterTypeChecker(field.type);
89
+ }, true);
90
+ }
91
+
92
+ /**
93
+ * Check if a provided ABI type has the correct attributes and their associated types.
94
+ * This function compares the given 'type' object's keys with the expected attribute types
95
+ * specified in 'incompleteAttributes', as well as the required 'kind' property.
96
+ * Throws an error if there are any unrecognised attributes or incorrect attribute types.
97
+ *
98
+ * @param type - The ABI type object to be checked for correct attributes.
99
+ * @param incompleteAttributes - An object representing the expected attribute types without the 'kind' property.
100
+ * @returns Returns true if the provided ABI type has the correct attributes and their associated types, otherwise throws an error.
101
+ */
102
+ function checkAttributes<T extends BasicType<string>>(type: T, incompleteAttributes: TypeWithoutKind<T>) {
103
+ const typeKeys = Object.keys(type);
104
+ const attributes = { ...incompleteAttributes, kind: 'string' };
105
+
106
+ if (typeKeys.length !== Object.keys(attributes).length) {
107
+ throw new Error(`Unrecognised attribute on type ${type.kind}`);
108
+ }
109
+
110
+ typeKeys.forEach(element => {
111
+ if (!(element in type && typeof (type as any)[element] === (attributes as any)[element])) {
112
+ throw new Error(`ABI function parameter has an incorrectly formed ${type.kind}`);
113
+ }
114
+ });
115
+
116
+ return true;
117
+ }
@@ -0,0 +1,160 @@
1
+ import { AztecAddress, DeployedContract, EthAddress, NodeInfo, Tx, TxHash, TxReceipt } from '@aztec/aztec-rpc';
2
+ import { MockProxy, mock } from 'jest-mock-extended';
3
+
4
+ import { ABIParameterVisibility, ContractAbi, FunctionType } from '@aztec/foundation/abi';
5
+ import { randomBytes } from '@aztec/foundation/crypto';
6
+ import { TxExecutionRequest } from '@aztec/types';
7
+ import { Wallet } from '../aztec_rpc_client/wallet.js';
8
+ import { Contract } from './contract.js';
9
+
10
+ describe('Contract Class', () => {
11
+ let wallet: MockProxy<Wallet>;
12
+
13
+ const contractAddress = AztecAddress.random();
14
+ const account = AztecAddress.random();
15
+
16
+ const mockTx = { type: 'Tx' } as any as Tx;
17
+ const mockTxRequest = { type: 'TxRequest' } as any as TxExecutionRequest;
18
+ const mockTxHash = { type: 'TxHash' } as any as TxHash;
19
+ const mockTxReceipt = { type: 'TxReceipt' } as any as TxReceipt;
20
+ const mockViewResultValue = 1;
21
+ const mockNodeInfo: NodeInfo = { version: 1, chainId: 2 };
22
+
23
+ const defaultAbi: ContractAbi = {
24
+ name: 'FooContract',
25
+ functions: [
26
+ {
27
+ name: 'bar',
28
+ functionType: FunctionType.SECRET,
29
+ parameters: [
30
+ {
31
+ name: 'value',
32
+ type: {
33
+ kind: 'field',
34
+ },
35
+ visibility: ABIParameterVisibility.PUBLIC,
36
+ },
37
+ {
38
+ name: 'value',
39
+ type: {
40
+ kind: 'field',
41
+ },
42
+ visibility: ABIParameterVisibility.SECRET,
43
+ },
44
+ ],
45
+ returnTypes: [],
46
+ bytecode: '0af',
47
+ },
48
+ {
49
+ name: 'baz',
50
+ functionType: FunctionType.OPEN,
51
+ parameters: [],
52
+ returnTypes: [],
53
+ bytecode: '0be',
54
+ },
55
+ {
56
+ name: 'qux',
57
+ functionType: FunctionType.UNCONSTRAINED,
58
+ parameters: [
59
+ {
60
+ name: 'value',
61
+ type: {
62
+ kind: 'field',
63
+ },
64
+ visibility: ABIParameterVisibility.PUBLIC,
65
+ },
66
+ ],
67
+ returnTypes: [
68
+ {
69
+ kind: 'integer',
70
+ sign: '',
71
+ width: 32,
72
+ },
73
+ ],
74
+ bytecode: '0cd',
75
+ },
76
+ ],
77
+ };
78
+
79
+ const randomContractAbi = (): ContractAbi => ({
80
+ name: randomBytes(4).toString('hex'),
81
+ functions: [],
82
+ });
83
+
84
+ const randomDeployContract = (): DeployedContract => ({
85
+ abi: randomContractAbi(),
86
+ address: AztecAddress.random(),
87
+ portalContract: EthAddress.random(),
88
+ });
89
+
90
+ beforeEach(() => {
91
+ wallet = mock<Wallet>();
92
+ wallet.createAuthenticatedTxRequest.mockResolvedValue(mockTxRequest);
93
+ wallet.sendTx.mockResolvedValue(mockTxHash);
94
+ wallet.viewTx.mockResolvedValue(mockViewResultValue);
95
+ wallet.getTxReceipt.mockResolvedValue(mockTxReceipt);
96
+ wallet.getNodeInfo.mockResolvedValue(mockNodeInfo);
97
+ wallet.simulateTx.mockResolvedValue(mockTx);
98
+ });
99
+
100
+ it('should create and send a contract method tx', async () => {
101
+ const fooContract = new Contract(contractAddress, defaultAbi, wallet);
102
+ const param0 = 12;
103
+ const param1 = 345n;
104
+ const sentTx = fooContract.methods.bar(param0, param1).send({
105
+ from: account,
106
+ });
107
+ const txHash = await sentTx.getTxHash();
108
+ const receipt = await sentTx.getReceipt();
109
+
110
+ expect(txHash).toBe(mockTxHash);
111
+ expect(receipt).toBe(mockTxReceipt);
112
+ expect(wallet.createAuthenticatedTxRequest).toHaveBeenCalledTimes(1);
113
+ expect(wallet.sendTx).toHaveBeenCalledTimes(1);
114
+ expect(wallet.sendTx).toHaveBeenCalledWith(mockTx);
115
+ });
116
+
117
+ it('should call view on an unconstrained function', async () => {
118
+ const fooContract = new Contract(contractAddress, defaultAbi, wallet);
119
+ const result = await fooContract.methods.qux(123n).view({
120
+ from: account,
121
+ });
122
+ expect(wallet.viewTx).toHaveBeenCalledTimes(1);
123
+ expect(wallet.viewTx).toHaveBeenCalledWith('qux', [123n], contractAddress, account);
124
+ expect(result).toBe(mockViewResultValue);
125
+ });
126
+
127
+ it('should not call send on an unconstrained function', () => {
128
+ const fooContract = new Contract(contractAddress, defaultAbi, wallet);
129
+ expect(() =>
130
+ fooContract.methods.qux().send({
131
+ from: account,
132
+ }),
133
+ ).toThrow();
134
+ });
135
+
136
+ it('should not call view on a secret or open function', () => {
137
+ const fooContract = new Contract(contractAddress, defaultAbi, wallet);
138
+ expect(() => fooContract.methods.bar().view()).toThrow();
139
+ expect(() => fooContract.methods.baz().view()).toThrow();
140
+ });
141
+
142
+ it('should add contract and dependencies to aztec rpc', async () => {
143
+ const entry = randomDeployContract();
144
+ const contract = new Contract(entry.address, entry.abi, wallet);
145
+
146
+ {
147
+ await contract.attach(entry.portalContract);
148
+ expect(wallet.addContracts).toHaveBeenCalledTimes(1);
149
+ expect(wallet.addContracts).toHaveBeenCalledWith([entry]);
150
+ wallet.addContracts.mockClear();
151
+ }
152
+
153
+ {
154
+ const dependencies = [randomDeployContract(), randomDeployContract()];
155
+ await contract.attach(entry.portalContract, dependencies);
156
+ expect(wallet.addContracts).toHaveBeenCalledTimes(1);
157
+ expect(wallet.addContracts).toHaveBeenCalledWith([entry, ...dependencies]);
158
+ }
159
+ });
160
+ });
@@ -0,0 +1,80 @@
1
+ import { DeployedContract, generateFunctionSelector } from '@aztec/aztec-rpc';
2
+ import { ContractAbi, FunctionAbi } from '@aztec/foundation/abi';
3
+ import { AztecAddress } from '@aztec/foundation/aztec-address';
4
+ import { EthAddress } from '@aztec/foundation/eth-address';
5
+ import { Wallet } from '../aztec_rpc_client/wallet.js';
6
+ import { ContractFunctionInteraction } from './contract_function_interaction.js';
7
+
8
+ /**
9
+ * Type representing a contract method that returns a ContractFunctionInteraction instance
10
+ * and has a readonly 'selector' property of type Buffer. Takes any number of arguments.
11
+ */
12
+ type ContractMethod = ((...args: any[]) => ContractFunctionInteraction) & {
13
+ /**
14
+ * The unique identifier for a contract function in bytecode.
15
+ */
16
+ readonly selector: Buffer;
17
+ };
18
+
19
+ /**
20
+ * The Contract class represents a contract and provides utility methods for interacting with it.
21
+ * It enables the creation of ContractFunctionInteraction instances for each function in the contract's ABI,
22
+ * allowing users to call or send transactions to these functions. Additionally, the Contract class can be used
23
+ * to attach the contract instance to a deployed contract on-chain through the AztecRPCClient, which facilitates
24
+ * interaction with Aztec's privacy protocol.
25
+ */
26
+ export class Contract {
27
+ /**
28
+ * An object containing contract methods mapped to their respective names.
29
+ */
30
+ public methods: { [name: string]: ContractMethod } = {};
31
+
32
+ constructor(
33
+ /**
34
+ * The deployed contract's address.
35
+ */
36
+ public readonly address: AztecAddress,
37
+ /**
38
+ * The Application Binary Interface for the contract.
39
+ */
40
+ public readonly abi: ContractAbi,
41
+ /**
42
+ * The wallet.
43
+ */
44
+ private wallet: Wallet,
45
+ ) {
46
+ abi.functions.forEach((f: FunctionAbi) => {
47
+ const interactionFunction = (...args: any[]) => {
48
+ return new ContractFunctionInteraction(this.wallet, this.address!, f, args);
49
+ };
50
+
51
+ this.methods[f.name] = Object.assign(interactionFunction, {
52
+ /**
53
+ * A getter for users to fetch the function selector.
54
+ * @returns Selector of the function.
55
+ */
56
+ get selector() {
57
+ return generateFunctionSelector(f.name, f.parameters);
58
+ },
59
+ });
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Attach the current contract instance to a portal contract and optionally add its dependencies.
65
+ * The function will return a promise that resolves when all contracts have been added to the AztecRPCClient.
66
+ * This is useful when you need to interact with a deployed contract that has multiple nested contracts.
67
+ *
68
+ * @param portalContract - The Ethereum address of the portal contract.
69
+ * @param dependencies - An optional array of additional DeployedContract instances to be attached.
70
+ * @returns A promise that resolves when all contracts are successfully added to the AztecRPCClient.
71
+ */
72
+ attach(portalContract: EthAddress, dependencies: DeployedContract[] = []) {
73
+ const deployedContract: DeployedContract = {
74
+ abi: this.abi,
75
+ address: this.address,
76
+ portalContract,
77
+ };
78
+ return this.wallet.addContracts([deployedContract, ...dependencies]);
79
+ }
80
+ }
@@ -0,0 +1,141 @@
1
+ import { encodeArguments } from '@aztec/acir-simulator';
2
+ import { generateFunctionSelector, Tx } from '@aztec/aztec-rpc';
3
+ import { AztecAddress, Fr, FunctionData, TxContext } from '@aztec/circuits.js';
4
+ import { FunctionAbi, FunctionType } from '@aztec/foundation/abi';
5
+ import { ExecutionRequest, TxExecutionRequest } from '@aztec/types';
6
+ import { Wallet } from '../aztec_rpc_client/wallet.js';
7
+ import { SentTx } from './sent_tx.js';
8
+
9
+ /**
10
+ * Represents options for calling a (constrained) function in a contract.
11
+ * Allows the user to specify the sender address and nonce for a transaction.
12
+ */
13
+ export interface SendMethodOptions {
14
+ /**
15
+ * Sender's address initiating the transaction.
16
+ */
17
+ from?: AztecAddress;
18
+ /**
19
+ * The nonce representing the order of transactions sent by the address.
20
+ */
21
+ nonce?: Fr;
22
+ }
23
+
24
+ /**
25
+ * Represents the options for a view method in a contract function interaction.
26
+ * Allows specifying the address from which the view method should be called.
27
+ */
28
+ export interface ViewMethodOptions {
29
+ /**
30
+ * The sender's Aztec address.
31
+ */
32
+ from?: AztecAddress;
33
+ }
34
+
35
+ /**
36
+ * This is the class that is returned when calling e.g. `contract.methods.myMethod(arg0, arg1)`.
37
+ * It contains available interactions one can call on a method.
38
+ */
39
+ export class ContractFunctionInteraction {
40
+ protected tx?: Tx;
41
+ protected txRequest?: TxExecutionRequest;
42
+
43
+ constructor(
44
+ protected wallet: Wallet,
45
+ protected contractAddress: AztecAddress,
46
+ protected functionDao: FunctionAbi,
47
+ protected args: any[],
48
+ ) {}
49
+
50
+ /**
51
+ * Create an Aztec transaction instance by combining the transaction request and its signature.
52
+ * This function will first check if a signature exists, and if not, it will call the `sign` method
53
+ * to obtain the signature before creating the transaction. Throws an error if the function is
54
+ * of unconstrained type or if the transaction request and signature are missing.
55
+ *
56
+ * @param options - An optional object containing additional configuration for the transaction.
57
+ * @returns A Promise that resolves to a transaction instance.
58
+ */
59
+ public async create(options: SendMethodOptions = {}): Promise<TxExecutionRequest> {
60
+ if (this.functionDao.functionType === FunctionType.UNCONSTRAINED) {
61
+ throw new Error("Can't call `create` on an unconstrained function.");
62
+ }
63
+ if (!this.txRequest) {
64
+ const executionRequest = this.getExecutionRequest(this.contractAddress, options.from);
65
+ const nodeInfo = await this.wallet.getNodeInfo();
66
+ const txContext = TxContext.empty(new Fr(nodeInfo.chainId), new Fr(nodeInfo.version));
67
+ const txRequest = await this.wallet.createAuthenticatedTxRequest([executionRequest], txContext);
68
+ this.txRequest = txRequest;
69
+ }
70
+ return this.txRequest;
71
+ }
72
+
73
+ /**
74
+ * Simulates a transaction's execution.
75
+ * @param options - optional arguments to be used in the creation of the transaction
76
+ * @returns The resulting transaction
77
+ */
78
+ public async simulate(options: SendMethodOptions): Promise<Tx> {
79
+ const txRequest = this.txRequest ?? (await this.create(options));
80
+ // TODO: Why do we need from separately, and cannot get it from txRequest.origin? When would they differ?
81
+ this.tx = await this.wallet.simulateTx(txRequest, txRequest.origin);
82
+ return this.tx;
83
+ }
84
+
85
+ protected getExecutionRequest(to: AztecAddress, from?: AztecAddress): ExecutionRequest {
86
+ const flatArgs = encodeArguments(this.functionDao, this.args);
87
+
88
+ const functionData = new FunctionData(
89
+ generateFunctionSelector(this.functionDao.name, this.functionDao.parameters),
90
+ this.functionDao.functionType === FunctionType.SECRET,
91
+ this.functionDao.name === 'constructor',
92
+ );
93
+
94
+ return {
95
+ args: flatArgs,
96
+ functionData,
97
+ to,
98
+ from: from || AztecAddress.ZERO,
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Sends a transaction to the contract function with the specified options.
104
+ * This function throws an error if called on an unconstrained function.
105
+ * It creates and signs the transaction if necessary, and returns a SentTx instance,
106
+ * which can be used to track the transaction status, receipt, and events.
107
+ *
108
+ * @param options - An optional object containing 'from' property representing
109
+ * the AztecAddress of the sender. If not provided, the default address is used.
110
+ * @returns A SentTx instance for tracking the transaction status and information.
111
+ */
112
+ public send(options: SendMethodOptions = {}) {
113
+ if (this.functionDao.functionType === FunctionType.UNCONSTRAINED) {
114
+ throw new Error("Can't call `send` on an unconstrained function.");
115
+ }
116
+
117
+ const promise = (async () => {
118
+ const tx = this.tx ?? (await this.simulate(options));
119
+ return this.wallet.sendTx(tx);
120
+ })();
121
+
122
+ return new SentTx(this.wallet, promise);
123
+ }
124
+
125
+ /**
126
+ * Execute a view (read-only) transaction on an unconstrained function.
127
+ * This method is used to call functions that do not modify the contract state and only return data.
128
+ * Throws an error if called on a non-unconstrained function.
129
+ *
130
+ * @param options - An optional object containing additional configuration for the transaction.
131
+ * @returns The result of the view transaction as returned by the contract function.
132
+ */
133
+ public view(options: ViewMethodOptions = {}) {
134
+ if (this.functionDao.functionType !== FunctionType.UNCONSTRAINED) {
135
+ throw new Error('Can only call `view` on an unconstrained function.');
136
+ }
137
+
138
+ const { from } = options;
139
+ return this.wallet.viewTx(this.functionDao.name, this.args, this.contractAddress, from);
140
+ }
141
+ }
@@ -0,0 +1,3 @@
1
+ export * from './contract.js';
2
+ export * from './contract_function_interaction.js';
3
+ export * from './sent_tx.js';