@lfdecentralizedtrust/zeto-contracts 0.2.1 → 0.2.2

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.
@@ -0,0 +1,156 @@
1
+ // Copyright © 2026 Kaleido, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ //
5
+ // Licensed under the Apache License, Version 2.0 (the "License");
6
+ // you may not use this file except in compliance with the License.
7
+ // You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing, software
12
+ // distributed under the License is distributed on an "AS IS" BASIS,
13
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ // See the License for the specific language governing permissions and
15
+ // limitations under the License.
16
+ pragma solidity ^0.8.27;
17
+
18
+ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
19
+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
20
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
21
+ import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
22
+ import {IZetoInitializable} from "./lib/interfaces/izeto_initializable.sol";
23
+
24
+ contract ZetoTokenFactoryUpgradeable is Initializable, OwnableUpgradeable, UUPSUpgradeable {
25
+ // all the addresses needed by the factory to
26
+ // clone a Zeto token and initialize it. The
27
+ // "implementation" is used to clone the token,
28
+ // the rest of the addresses are used to initialize
29
+ struct ImplementationInfo {
30
+ address implementation;
31
+ IZetoInitializable.VerifiersInfo verifiers;
32
+ }
33
+
34
+ /// @custom:storage-location erc7201:zeto.storage.ZetoTokenFactory
35
+ struct ZetoTokenFactoryStorage {
36
+ mapping(string => ImplementationInfo) implementations;
37
+ }
38
+
39
+ // keccak256(abi.encode(uint256(keccak256("zeto.storage.ZetoTokenFactory")) - 1)) & ~bytes32(uint256(0xff))
40
+ bytes32 private constant ZETO_TOKEN_FACTORY_STORAGE_LOCATION =
41
+ 0xea00970f7a43a8afae26fee4308a7f177a9692a756b610fca8a1c058954e4800;
42
+
43
+ event ZetoTokenDeployed(address indexed zetoToken);
44
+
45
+ function _getZetoTokenFactoryStorage()
46
+ private
47
+ pure
48
+ returns (ZetoTokenFactoryStorage storage $)
49
+ {
50
+ assembly {
51
+ $.slot := ZETO_TOKEN_FACTORY_STORAGE_LOCATION
52
+ }
53
+ }
54
+
55
+ /// @custom:oz-upgrades-unsafe-allow constructor
56
+ constructor() {
57
+ _disableInitializers();
58
+ }
59
+
60
+ function initialize() public virtual initializer {
61
+ __Ownable_init(_msgSender());
62
+ __UUPSUpgradeable_init();
63
+ }
64
+
65
+ function registerImplementation(
66
+ string memory name,
67
+ ImplementationInfo memory implementation
68
+ ) public onlyOwner {
69
+ require(
70
+ implementation.implementation != address(0),
71
+ "Factory: implementation address is required"
72
+ );
73
+ require(
74
+ implementation.verifiers.verifier != address(0),
75
+ "Factory: verifier address is required"
76
+ );
77
+ // the depositVerifier and withdrawVerifier are optional
78
+ // for the non-fungible token implementations
79
+ ZetoTokenFactoryStorage storage $ = _getZetoTokenFactoryStorage();
80
+ $.implementations[name] = implementation;
81
+ }
82
+
83
+ function deployZetoFungibleToken(
84
+ string memory name,
85
+ string memory symbol,
86
+ string memory tokenImplementation,
87
+ address initialOwner
88
+ ) public returns (address) {
89
+ ZetoTokenFactoryStorage storage $ = _getZetoTokenFactoryStorage();
90
+ ImplementationInfo memory args = $.implementations[tokenImplementation];
91
+ require(
92
+ args.implementation != address(0),
93
+ "Factory: failed to find implementation"
94
+ );
95
+ // check that the registered implementation is for a fungible token
96
+ // and has the required verifier addresses
97
+ require(
98
+ args.verifiers.depositVerifier != address(0),
99
+ "Factory: depositVerifier address is required"
100
+ );
101
+ require(
102
+ args.verifiers.withdrawVerifier != address(0),
103
+ "Factory: withdrawVerifier address is required"
104
+ );
105
+ require(
106
+ args.verifiers.batchVerifier != address(0),
107
+ "Factory: batchVerifier address is required"
108
+ );
109
+ require(
110
+ args.verifiers.batchWithdrawVerifier != address(0),
111
+ "Factory: batchWithdrawVerifier address is required"
112
+ );
113
+ address instance = Clones.clone(args.implementation);
114
+ require(
115
+ instance != address(0),
116
+ "Factory: failed to clone implementation"
117
+ );
118
+ (IZetoInitializable(instance)).initialize(
119
+ name,
120
+ symbol,
121
+ initialOwner,
122
+ args.verifiers
123
+ );
124
+ emit ZetoTokenDeployed(instance);
125
+ return instance;
126
+ }
127
+
128
+ function deployZetoNonFungibleToken(
129
+ string memory name,
130
+ string memory symbol,
131
+ string memory tokenImplementation,
132
+ address initialOwner
133
+ ) public returns (address) {
134
+ ZetoTokenFactoryStorage storage $ = _getZetoTokenFactoryStorage();
135
+ ImplementationInfo memory args = $.implementations[tokenImplementation];
136
+ require(
137
+ args.implementation != address(0),
138
+ "Factory: failed to find implementation"
139
+ );
140
+ address instance = Clones.clone(args.implementation);
141
+ require(
142
+ instance != address(0),
143
+ "Factory: failed to clone implementation"
144
+ );
145
+ (IZetoInitializable(instance)).initialize(
146
+ name,
147
+ symbol,
148
+ initialOwner,
149
+ args.verifiers
150
+ );
151
+ emit ZetoTokenDeployed(instance);
152
+ return instance;
153
+ }
154
+
155
+ function _authorizeUpgrade(address) internal override onlyOwner {}
156
+ }
@@ -16,8 +16,9 @@
16
16
  pragma solidity ^0.8.27;
17
17
 
18
18
  import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
19
- import {SmtLib} from "@iden3/contracts/lib/SmtLib.sol";
20
- import {PoseidonUnit2L, PoseidonUnit3L} from "@iden3/contracts/lib/Poseidon.sol";
19
+ import {SmtLib} from "@iden3/contracts/contracts/lib/SmtLib.sol";
20
+ import {PoseidonHasher} from "@iden3/contracts/contracts/lib/hash/PoseidonHasher.sol";
21
+ import {PoseidonUnit2L, PoseidonUnit3L} from "@iden3/contracts/contracts/lib/Poseidon.sol";
21
22
  import {Commonlib} from "./common.sol";
22
23
  import {IZeto} from "./interfaces/izeto.sol";
23
24
  import {IZetoKyc} from "./interfaces/izeto_kyc.sol";
@@ -38,6 +39,7 @@ abstract contract Registry is OwnableUpgradeable, IZetoKyc {
38
39
 
39
40
  function __Registry_init() internal onlyInitializing {
40
41
  _publicKeysTree.initialize(MAX_SMT_DEPTH);
42
+ _publicKeysTree.setHasher(new PoseidonHasher());
41
43
  }
42
44
 
43
45
  function register(
@@ -20,8 +20,10 @@ import {IZeto} from "./interfaces/izeto.sol";
20
20
  import {MAX_SMT_DEPTH} from "./interfaces/izeto.sol";
21
21
  import {IZetoLockable} from "./interfaces/izeto_lockable.sol";
22
22
  import {ZetoCommon} from "./zeto_common.sol";
23
- import {SmtLib} from "@iden3/contracts/lib/SmtLib.sol";
24
- import {PoseidonUnit3L} from "@iden3/contracts/lib/Poseidon.sol";
23
+ import {SmtLib} from "@iden3/contracts/contracts/lib/SmtLib.sol";
24
+ import {PoseidonHasher} from "@iden3/contracts/contracts/lib/hash/PoseidonHasher.sol";
25
+ import {IHasher} from "@iden3/contracts/contracts/interfaces/IHasher.sol";
26
+ import {PoseidonUnit3L} from "@iden3/contracts/contracts/lib/Poseidon.sol";
25
27
  import {console} from "hardhat/console.sol";
26
28
 
27
29
  /// @title A sample base implementation of a Zeto based token contract with nullifiers
@@ -38,6 +40,7 @@ abstract contract ZetoNullifier is IZeto, IZetoLockable, ZetoCommon {
38
40
  SmtLib.Data internal _lockedCommitmentsTree;
39
41
  using SmtLib for SmtLib.Data;
40
42
  mapping(uint256 => bool) private _nullifiers;
43
+ IHasher private _hasher;
41
44
 
42
45
  error UTXORootNotFound(uint256 root);
43
46
 
@@ -49,6 +52,9 @@ abstract contract ZetoNullifier is IZeto, IZetoLockable, ZetoCommon {
49
52
  __ZetoCommon_init(name_, symbol_, initialOwner);
50
53
  _commitmentsTree.initialize(MAX_SMT_DEPTH);
51
54
  _lockedCommitmentsTree.initialize(MAX_SMT_DEPTH);
55
+ _hasher = new PoseidonHasher();
56
+ _commitmentsTree.setHasher(_hasher);
57
+ _lockedCommitmentsTree.setHasher(_hasher);
52
58
  }
53
59
 
54
60
  function validateTransactionProposal(
@@ -15,8 +15,8 @@
15
15
  // limitations under the License.
16
16
  pragma solidity ^0.8.27;
17
17
 
18
- import {SmtLib} from "@iden3/contracts/lib/SmtLib.sol";
19
- import {PoseidonUnit3L} from "@iden3/contracts/lib/Poseidon.sol";
18
+ import {SmtLib} from "@iden3/contracts/contracts/lib/SmtLib.sol";
19
+ import {PoseidonUnit3L} from "@iden3/contracts/contracts/lib/Poseidon.sol";
20
20
 
21
21
  // test ground for better understanding of the SmtLib library implementation
22
22
  contract TestSmt {
@@ -15,7 +15,7 @@
15
15
  // limitations under the License.
16
16
  pragma solidity ^0.8.27;
17
17
 
18
- import {PoseidonUnit5L} from "@iden3/contracts/lib/Poseidon.sol";
18
+ import {PoseidonUnit5L} from "@iden3/contracts/contracts/lib/Poseidon.sol";
19
19
  import {IZeto} from "./lib/interfaces/izeto.sol";
20
20
  import {MAX_BATCH} from "./lib/interfaces/izeto.sol";
21
21
  import {Groth16Verifier_Deposit} from "./verifiers/verifier_deposit.sol";
@@ -35,6 +35,7 @@ import {console} from "hardhat/console.sol";
35
35
  uint256 constant INPUT_SIZE = 9;
36
36
  // uint256 constant INPUT_SIZE_LOCKED = 8;
37
37
  uint256 constant BATCH_INPUT_SIZE = 33;
38
+
38
39
  // uint256 constant BATCH_INPUT_SIZE_LOCKED = 32;
39
40
 
40
41
  /// @title A sample implementation of a Zeto based fungible token with anonymity and history masking
@@ -54,6 +55,7 @@ contract Zeto_AnonNullifierQurrency is
54
55
  {
55
56
  Groth16Verifier_AnonNullifierQurrencyTransfer internal _verifier;
56
57
  Groth16Verifier_AnonNullifierQurrencyTransferBatch internal _batchVerifier;
58
+
57
59
  // Groth16Verifier_AnonNullifierTransferLocked internal _lockVerifier;
58
60
  // Groth16Verifier_AnonNullifierTransferLockedBatch
59
61
  // internal _batchLockVerifier;
@@ -23,12 +23,7 @@ export const SmtLibModule = buildModule("SmtLib", (m) => {
23
23
  const poseidon3 = m.library("Poseidon3", PoseidonArtifact(3));
24
24
  const poseidon5 = m.library("Poseidon5", PoseidonArtifact(5));
25
25
  const poseidon6 = m.library("Poseidon6", PoseidonArtifact(6));
26
- const smtLib = m.contract("SmtLib", [], {
27
- libraries: {
28
- PoseidonUnit2L: poseidon2,
29
- PoseidonUnit3L: poseidon3,
30
- },
31
- });
26
+ const smtLib = m.contract("SmtLib", []);
32
27
  return { smtLib, poseidon2, poseidon3, poseidon5, poseidon6 };
33
28
  });
34
29
 
@@ -36,7 +36,7 @@ const BatchVerifierModule = buildModule(
36
36
  );
37
37
 
38
38
  export default buildModule("Zeto_AnonEncNullifier", (m) => {
39
- const { smtLib, poseidon3 } = m.useModule(SmtLibModule);
39
+ const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule);
40
40
  const { verifier } = m.useModule(VerifierModule);
41
41
  const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
42
42
  const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
@@ -54,6 +54,7 @@ export default buildModule("Zeto_AnonEncNullifier", (m) => {
54
54
  batchVerifier,
55
55
  batchWithdrawVerifier,
56
56
  smtLib,
57
+ poseidon2,
57
58
  poseidon3,
58
59
  };
59
60
  });
@@ -45,7 +45,7 @@ const BatchVerifierModule = buildModule(
45
45
  );
46
46
 
47
47
  export default buildModule("Zeto_AnonEncNullifierNonRepudiation", (m) => {
48
- const { smtLib, poseidon3 } = m.useModule(SmtLibModule);
48
+ const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule);
49
49
  const { verifier } = m.useModule(VerifierModule);
50
50
  const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
51
51
  const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
@@ -62,6 +62,7 @@ export default buildModule("Zeto_AnonEncNullifierNonRepudiation", (m) => {
62
62
  batchVerifier,
63
63
  batchWithdrawVerifier,
64
64
  smtLib,
65
+ poseidon2,
65
66
  poseidon3,
66
67
  };
67
68
  });
@@ -64,7 +64,7 @@ const BatchLockVerifierModule = buildModule(
64
64
  );
65
65
 
66
66
  export default buildModule("Zeto_AnonNullifier", (m) => {
67
- const { smtLib, poseidon3 } = m.useModule(SmtLibModule);
67
+ const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule);
68
68
  const { verifier } = m.useModule(VerifierModule);
69
69
  const { verifier: lockVerifier } = m.useModule(LockVerifierModule);
70
70
  const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
@@ -86,6 +86,7 @@ export default buildModule("Zeto_AnonNullifier", (m) => {
86
86
  batchLockVerifier,
87
87
  batchWithdrawVerifier,
88
88
  smtLib,
89
+ poseidon2,
89
90
  poseidon3,
90
91
  };
91
92
  });
@@ -68,7 +68,7 @@ const BatchBurnNullifierVerifierModule = buildModule("Groth16Verifier_BurnNullif
68
68
  });
69
69
 
70
70
  export default buildModule("Zeto_AnonNullifier", (m) => {
71
- const { smtLib, poseidon3 } = m.useModule(SmtLibModule);
71
+ const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule);
72
72
  const { verifier } = m.useModule(VerifierModule);
73
73
  const { verifier: lockVerifier } = m.useModule(LockVerifierModule);
74
74
  const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
@@ -98,6 +98,7 @@ export default buildModule("Zeto_AnonNullifier", (m) => {
98
98
  burnVerifier,
99
99
  batchBurnVerifier,
100
100
  smtLib,
101
+ poseidon2,
101
102
  poseidon3,
102
103
  };
103
104
  });
@@ -64,7 +64,7 @@ const BatchVerifierModule = buildModule(
64
64
  // );
65
65
 
66
66
  export default buildModule("Zeto_AnonNullifierQurrency", (m) => {
67
- const { smtLib, poseidon3, poseidon5, poseidon6 } = m.useModule(SmtLibModule);
67
+ const { smtLib, poseidon2, poseidon3, poseidon5, poseidon6 } = m.useModule(SmtLibModule);
68
68
  const { verifier } = m.useModule(VerifierModule);
69
69
  // const { verifier: lockVerifier } = m.useModule(LockVerifierModule);
70
70
  const { verifier: batchVerifier } = m.useModule(BatchVerifierModule);
@@ -86,6 +86,7 @@ export default buildModule("Zeto_AnonNullifierQurrency", (m) => {
86
86
  // batchLockVerifier,
87
87
  batchWithdrawVerifier,
88
88
  smtLib,
89
+ poseidon2,
89
90
  poseidon3,
90
91
  poseidon5,
91
92
  poseidon6,
@@ -37,9 +37,9 @@ const LockVerifierModule = buildModule(
37
37
  );
38
38
 
39
39
  export default buildModule("Zeto_NfAnonNullifier", (m) => {
40
- const { smtLib, poseidon3 } = m.useModule(SmtLibModule);
40
+ const { smtLib, poseidon2, poseidon3 } = m.useModule(SmtLibModule);
41
41
  const { verifier } = m.useModule(VerifierModule);
42
42
  const { verifier: lockVerifier } = m.useModule(LockVerifierModule);
43
43
 
44
- return { verifier, lockVerifier, smtLib, poseidon3 };
44
+ return { verifier, lockVerifier, smtLib, poseidon2, poseidon3 };
45
45
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lfdecentralizedtrust/zeto-contracts",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Zero knowledge proof based UTXO tokens toolkit for fungible or non-fungible assets",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -27,7 +27,7 @@
27
27
  "zeto-js": "file:../zkp/js"
28
28
  },
29
29
  "dependencies": {
30
- "@iden3/contracts": "^2.5.0",
30
+ "@iden3/contracts": "git+https://github.com/kaleido-io/contracts.git#keccak256",
31
31
  "@openzeppelin/contracts": "^5.1.0",
32
32
  "@openzeppelin/contracts-upgradeable": "^5.0.2",
33
33
  "@openzeppelin/hardhat-upgrades": "^3.2.1",
@@ -27,6 +27,7 @@ export async function deployDependencies() {
27
27
  batchVerifier,
28
28
  batchWithdrawVerifier,
29
29
  smtLib,
30
+ poseidon2,
30
31
  poseidon3,
31
32
  } = await ignition.deploy(zetoModule);
32
33
  return {
@@ -49,6 +50,7 @@ export async function deployDependencies() {
49
50
  ],
50
51
  libraries: {
51
52
  SmtLib: smtLib.target,
53
+ PoseidonUnit2L: poseidon2.target,
52
54
  PoseidonUnit3L: poseidon3.target,
53
55
  },
54
56
  };
@@ -27,6 +27,7 @@ export async function deployDependencies() {
27
27
  batchVerifier,
28
28
  batchWithdrawVerifier,
29
29
  smtLib,
30
+ poseidon2,
30
31
  poseidon3,
31
32
  } = await ignition.deploy(zetoModule);
32
33
  return {
@@ -49,6 +50,7 @@ export async function deployDependencies() {
49
50
  ],
50
51
  libraries: {
51
52
  SmtLib: smtLib.target,
53
+ PoseidonUnit2L: poseidon2.target,
52
54
  PoseidonUnit3L: poseidon3.target,
53
55
  },
54
56
  };
@@ -29,6 +29,7 @@ export async function deployDependencies() {
29
29
  batchLockVerifier,
30
30
  batchWithdrawVerifier,
31
31
  smtLib,
32
+ poseidon2,
32
33
  poseidon3,
33
34
  } = await ignition.deploy(zetoModule);
34
35
  return {
@@ -51,6 +52,7 @@ export async function deployDependencies() {
51
52
  ],
52
53
  libraries: {
53
54
  SmtLib: smtLib.target,
55
+ PoseidonUnit2L: poseidon2.target,
54
56
  PoseidonUnit3L: poseidon3.target,
55
57
  },
56
58
  };
@@ -31,6 +31,7 @@ export async function deployDependencies() {
31
31
  burnVerifier,
32
32
  batchBurnVerifier,
33
33
  smtLib,
34
+ poseidon2,
34
35
  poseidon3,
35
36
  } = await ignition.deploy(zetoModule);
36
37
  return {
@@ -53,6 +54,7 @@ export async function deployDependencies() {
53
54
  ],
54
55
  libraries: {
55
56
  SmtLib: smtLib.target,
57
+ PoseidonUnit2L: poseidon2.target,
56
58
  PoseidonUnit3L: poseidon3.target,
57
59
  },
58
60
  };
@@ -29,6 +29,7 @@ export async function deployDependencies() {
29
29
  // batchLockVerifier,
30
30
  batchWithdrawVerifier,
31
31
  smtLib,
32
+ poseidon2,
32
33
  poseidon3,
33
34
  poseidon5,
34
35
  poseidon6,
@@ -53,6 +54,7 @@ export async function deployDependencies() {
53
54
  ],
54
55
  libraries: {
55
56
  SmtLib: smtLib.target,
57
+ PoseidonUnit2L: poseidon2.target,
56
58
  PoseidonUnit3L: poseidon3.target,
57
59
  PoseidonUnit5L: poseidon5.target,
58
60
  PoseidonUnit6L: poseidon6.target,
@@ -20,7 +20,7 @@ import zetoModule from "../../ignition/modules/zeto_nf_anon_nullifier";
20
20
  export async function deployDependencies() {
21
21
  const [deployer] = await ethers.getSigners();
22
22
 
23
- const { verifier, lockVerifier, smtLib, poseidon3 } =
23
+ const { verifier, lockVerifier, smtLib, poseidon2, poseidon3 } =
24
24
  await ignition.deploy(zetoModule);
25
25
  return {
26
26
  deployer,
@@ -42,6 +42,7 @@ export async function deployDependencies() {
42
42
  ],
43
43
  libraries: {
44
44
  SmtLib: smtLib.target,
45
+ PoseidonUnit2L: poseidon2.target,
45
46
  PoseidonUnit3L: poseidon3.target,
46
47
  },
47
48
  };
@@ -0,0 +1,311 @@
1
+ // Copyright © 2026 Kaleido, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ //
5
+ // Licensed under the Apache License, Version 2.0 (the "License");
6
+ // you may not use this file except in compliance with the License.
7
+ // You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing, software
12
+ // distributed under the License is distributed on an "AS IS" BASIS,
13
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ // See the License for the specific language governing permissions and
15
+ // limitations under the License.
16
+
17
+ import { ethers, network, upgrades } from "hardhat";
18
+ import { Signer } from "ethers";
19
+ import { expect } from "chai";
20
+
21
+ describe("(factory upgradeable) Zeto based fungible token with anonymity without encryption or nullifier", function () {
22
+ let deployer: Signer;
23
+ let nonOwner: Signer;
24
+
25
+ before(async function () {
26
+ if (network.name !== "hardhat") {
27
+ // accommodate for longer block times on public networks
28
+ this.timeout(120000);
29
+ }
30
+ [deployer, nonOwner] = await ethers.getSigners();
31
+ });
32
+
33
+ async function deployFactory() {
34
+ const Factory = await ethers.getContractFactory(
35
+ "ZetoTokenFactoryUpgradeable",
36
+ );
37
+ const proxy = await upgrades.deployProxy(Factory, [], {
38
+ kind: "uups",
39
+ initializer: "initialize",
40
+ });
41
+ await proxy.waitForDeployment();
42
+ return await ethers.getContractAt(
43
+ "ZetoTokenFactoryUpgradeable",
44
+ await proxy.getAddress(),
45
+ );
46
+ }
47
+
48
+ it("attempting to register an implementation as a non-owner should fail", async function () {
49
+ const factory = await deployFactory();
50
+
51
+ const implInfo = {
52
+ implementation: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
53
+ verifiers: {
54
+ verifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
55
+ batchVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
56
+ depositVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
57
+ withdrawVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
58
+ batchWithdrawVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
59
+ lockVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
60
+ batchLockVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
61
+ burnVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
62
+ batchBurnVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
63
+ },
64
+ };
65
+ await expect(
66
+ factory.connect(nonOwner).registerImplementation("test", implInfo as any),
67
+ ).rejectedWith(`reverted with custom error 'OwnableUnauthorizedAccount(`);
68
+ });
69
+
70
+ it("attempting to initialize twice should fail", async function () {
71
+ const factory = await deployFactory();
72
+
73
+ await expect(factory.initialize()).rejectedWith("InvalidInitialization");
74
+ });
75
+
76
+ it("attempting to register an implementation without the required implementation value should fail", async function () {
77
+ const factory = await deployFactory();
78
+
79
+ const implInfo = {
80
+ implementation: "0x0000000000000000000000000000000000000000",
81
+ verifiers: {
82
+ verifier: "0x0000000000000000000000000000000000000000",
83
+ batchVerifier: "0x0000000000000000000000000000000000000000",
84
+ depositVerifier: "0x0000000000000000000000000000000000000000",
85
+ withdrawVerifier: "0x0000000000000000000000000000000000000000",
86
+ batchWithdrawVerifier: "0x0000000000000000000000000000000000000000",
87
+ lockVerifier: "0x0000000000000000000000000000000000000000",
88
+ batchLockVerifier: "0x0000000000000000000000000000000000000000",
89
+ burnVerifier: "0x0000000000000000000000000000000000000000",
90
+ batchBurnVerifier: "0x0000000000000000000000000000000000000000",
91
+ },
92
+ };
93
+ await expect(
94
+ factory.connect(deployer).registerImplementation("test", implInfo as any),
95
+ ).rejectedWith("Factory: implementation address is required");
96
+ });
97
+
98
+ it("attempting to register an implementation without the required verifier value should fail", async function () {
99
+ const factory = await deployFactory();
100
+
101
+ const implInfo = {
102
+ implementation: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
103
+ verifiers: {
104
+ verifier: "0x0000000000000000000000000000000000000000",
105
+ batchVerifier: "0x0000000000000000000000000000000000000000",
106
+ depositVerifier: "0x0000000000000000000000000000000000000000",
107
+ withdrawVerifier: "0x0000000000000000000000000000000000000000",
108
+ batchWithdrawVerifier: "0x0000000000000000000000000000000000000000",
109
+ lockVerifier: "0x0000000000000000000000000000000000000000",
110
+ batchLockVerifier: "0x0000000000000000000000000000000000000000",
111
+ burnVerifier: "0x0000000000000000000000000000000000000000",
112
+ batchBurnVerifier: "0x0000000000000000000000000000000000000000",
113
+ },
114
+ };
115
+ await expect(
116
+ factory.connect(deployer).registerImplementation("test", implInfo as any),
117
+ ).rejectedWith("Factory: verifier address is required");
118
+ });
119
+
120
+ it("attempting to register an implementation with the required values should succeed", async function () {
121
+ const factory = await deployFactory();
122
+
123
+ const implInfo = {
124
+ implementation: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
125
+ verifiers: {
126
+ verifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
127
+ batchVerifier: "0x0000000000000000000000000000000000000000",
128
+ depositVerifier: "0x0000000000000000000000000000000000000000",
129
+ withdrawVerifier: "0x0000000000000000000000000000000000000000",
130
+ batchWithdrawVerifier: "0x0000000000000000000000000000000000000000",
131
+ lockVerifier: "0x0000000000000000000000000000000000000000",
132
+ batchLockVerifier: "0x0000000000000000000000000000000000000000",
133
+ burnVerifier: "0x0000000000000000000000000000000000000000",
134
+ batchBurnVerifier: "0x0000000000000000000000000000000000000000",
135
+ },
136
+ };
137
+ await expect(
138
+ factory.connect(deployer).registerImplementation("test", implInfo as any),
139
+ ).fulfilled;
140
+ });
141
+
142
+ it("attempting to deploy a fungible token but with a registered implementation that misses required batchVerifier should fail", async function () {
143
+ const factory = await deployFactory();
144
+
145
+ const implInfo = {
146
+ implementation: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
147
+ verifiers: {
148
+ verifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
149
+ batchVerifier: "0x0000000000000000000000000000000000000000",
150
+ depositVerifier: "0x0000000000000000000000000000000000000000",
151
+ withdrawVerifier: "0x0000000000000000000000000000000000000000",
152
+ batchWithdrawVerifier: "0x0000000000000000000000000000000000000000",
153
+ lockVerifier: "0x0000000000000000000000000000000000000000",
154
+ batchLockVerifier: "0x0000000000000000000000000000000000000000",
155
+ burnVerifier: "0x0000000000000000000000000000000000000000",
156
+ batchBurnVerifier: "0x0000000000000000000000000000000000000000",
157
+ },
158
+ };
159
+ const tx1 = await factory
160
+ .connect(deployer)
161
+ .registerImplementation("test", implInfo as any);
162
+ await tx1.wait();
163
+
164
+ await expect(
165
+ factory
166
+ .connect(deployer)
167
+ .deployZetoFungibleToken(
168
+ "name",
169
+ "symbol",
170
+ "test",
171
+ await deployer.getAddress(),
172
+ ),
173
+ ).rejectedWith("Factory: depositVerifier address is required");
174
+ });
175
+
176
+ it("attempting to deploy a fungible token but with a registered implementation that misses required depositVerifier should fail", async function () {
177
+ const factory = await deployFactory();
178
+
179
+ const implInfo = {
180
+ implementation: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
181
+ verifiers: {
182
+ verifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
183
+ batchVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
184
+ depositVerifier: "0x0000000000000000000000000000000000000000",
185
+ withdrawVerifier: "0x0000000000000000000000000000000000000000",
186
+ batchWithdrawVerifier: "0x0000000000000000000000000000000000000000",
187
+ lockVerifier: "0x0000000000000000000000000000000000000000",
188
+ batchLockVerifier: "0x0000000000000000000000000000000000000000",
189
+ burnVerifier: "0x0000000000000000000000000000000000000000",
190
+ batchBurnVerifier: "0x0000000000000000000000000000000000000000",
191
+ },
192
+ };
193
+ const tx1 = await factory
194
+ .connect(deployer)
195
+ .registerImplementation("test", implInfo as any);
196
+ await tx1.wait();
197
+
198
+ await expect(
199
+ factory
200
+ .connect(deployer)
201
+ .deployZetoFungibleToken(
202
+ "name",
203
+ "symbol",
204
+ "test",
205
+ await deployer.getAddress(),
206
+ ),
207
+ ).rejectedWith("Factory: depositVerifier address is required");
208
+ });
209
+
210
+ it("attempting to deploy a fungible token but with a registered implementation that misses required withdrawVerifier should fail", async function () {
211
+ const factory = await deployFactory();
212
+
213
+ const implInfo = {
214
+ implementation: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
215
+ verifiers: {
216
+ verifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
217
+ batchVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
218
+ depositVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
219
+ withdrawVerifier: "0x0000000000000000000000000000000000000000",
220
+ batchWithdrawVerifier: "0x0000000000000000000000000000000000000000",
221
+ lockVerifier: "0x0000000000000000000000000000000000000000",
222
+ batchLockVerifier: "0x0000000000000000000000000000000000000000",
223
+ burnVerifier: "0x0000000000000000000000000000000000000000",
224
+ batchBurnVerifier: "0x0000000000000000000000000000000000000000",
225
+ },
226
+ };
227
+ const tx1 = await factory
228
+ .connect(deployer)
229
+ .registerImplementation("test", implInfo as any);
230
+ await tx1.wait();
231
+
232
+ await expect(
233
+ factory
234
+ .connect(deployer)
235
+ .deployZetoFungibleToken(
236
+ "name",
237
+ "symbol",
238
+ "test",
239
+ await deployer.getAddress(),
240
+ ),
241
+ ).rejectedWith("Factory: withdrawVerifier address is required");
242
+ });
243
+
244
+ it("attempting to deploy a fungible token but with a registered implementation that misses required batchWithdrawVerifier should fail", async function () {
245
+ const factory = await deployFactory();
246
+
247
+ const implInfo = {
248
+ implementation: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
249
+ verifiers: {
250
+ verifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
251
+ batchVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
252
+ depositVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
253
+ withdrawVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
254
+ batchWithdrawVerifier: "0x0000000000000000000000000000000000000000",
255
+ lockVerifier: "0x0000000000000000000000000000000000000000",
256
+ batchLockVerifier: "0x0000000000000000000000000000000000000000",
257
+ burnVerifier: "0x0000000000000000000000000000000000000000",
258
+ batchBurnVerifier: "0x0000000000000000000000000000000000000000",
259
+ },
260
+ };
261
+ const tx1 = await factory
262
+ .connect(deployer)
263
+ .registerImplementation("test", implInfo as any);
264
+ await tx1.wait();
265
+
266
+ await expect(
267
+ factory
268
+ .connect(deployer)
269
+ .deployZetoFungibleToken(
270
+ "name",
271
+ "symbol",
272
+ "test",
273
+ await deployer.getAddress(),
274
+ ),
275
+ ).rejectedWith("Factory: batchWithdrawVerifier address is required");
276
+ });
277
+
278
+ it("attempting to deploy a fungible token with a properly registered implementation should succeed", async function () {
279
+ const factory = await deployFactory();
280
+
281
+ const implInfo = {
282
+ implementation: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
283
+ verifiers: {
284
+ verifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
285
+ batchVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
286
+ depositVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
287
+ withdrawVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
288
+ batchWithdrawVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
289
+ lockVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
290
+ batchLockVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
291
+ burnVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
292
+ batchBurnVerifier: "0xae92d5aD7583AD66E49A0c67BAd18F6ba52dDDc1",
293
+ },
294
+ };
295
+ const tx1 = await factory
296
+ .connect(deployer)
297
+ .registerImplementation("test", implInfo as any);
298
+ await tx1.wait();
299
+
300
+ await expect(
301
+ factory
302
+ .connect(deployer)
303
+ .deployZetoFungibleToken(
304
+ "name",
305
+ "symbol",
306
+ "test",
307
+ await deployer.getAddress(),
308
+ ),
309
+ ).fulfilled;
310
+ });
311
+ });