@lfdecentralizedtrust/zeto-contracts 0.2.1 → 0.5.0
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.
- package/README.md +70 -162
- package/contracts/factory.sol +30 -34
- package/contracts/factory_upgradeable.sol +160 -0
- package/contracts/lib/common/util.sol +40 -0
- package/contracts/lib/interfaces/ILockableCapability.sol +318 -0
- package/contracts/lib/interfaces/{izeto.sol → IZeto.sol} +13 -1
- package/contracts/lib/interfaces/{izeto_initializable.sol → IZetoInitializable.sol} +14 -12
- package/contracts/lib/interfaces/{izeto_kyc.sol → IZetoKyc.sol} +3 -3
- package/contracts/lib/interfaces/IZetoLockableCapability.sol +237 -0
- package/contracts/lib/interfaces/IZetoStorage.sol +106 -0
- package/contracts/lib/interfaces/{izeto_lockable.sol → IZetoVerifier.sol} +8 -21
- package/contracts/lib/registry.sol +77 -27
- package/contracts/lib/storage/base.sol +210 -0
- package/contracts/lib/storage/nullifier.sol +166 -0
- package/contracts/lib/zeto_common.sol +277 -28
- package/contracts/lib/zeto_fungible.sol +451 -33
- package/contracts/lib/zeto_fungible_base.sol +69 -0
- package/contracts/lib/zeto_fungible_burn.sol +83 -53
- package/contracts/lib/zeto_fungible_burn_nullifier.sol +115 -72
- package/contracts/lib/zeto_fungible_nullifier.sol +167 -0
- package/contracts/lib/zeto_lockable.sol +613 -0
- package/contracts/lib/zeto_non_fungible.sol +236 -0
- package/contracts/lib/zeto_non_fungible_base.sol +61 -0
- package/contracts/lib/zeto_non_fungible_nullifier.sol +61 -0
- package/contracts/test/escrow1.sol +90 -34
- package/contracts/test/escrow2.sol +64 -29
- package/contracts/test/qurrency.sol +10 -2
- package/contracts/test/smt.sol +8 -3
- package/contracts/test/tendecimals.sol +14 -12
- package/contracts/verifiers/impl/anon.sol +189 -0
- package/contracts/verifiers/impl/anon_batch.sol +301 -0
- package/contracts/verifiers/impl/anon_enc.sol +266 -0
- package/contracts/verifiers/impl/anon_enc_batch.sol +602 -0
- package/contracts/verifiers/impl/anon_enc_nullifier.sol +287 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_batch.sol +679 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_kyc.sol +294 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_kyc_batch.sol +686 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation.sol +413 -0
- package/contracts/verifiers/impl/anon_enc_nullifier_non_repudiation_batch.sol +1141 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transfer.sol +217 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked.sol +196 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transferLocked_batch.sol +308 -0
- package/contracts/verifiers/impl/anon_nullifier_kyc_transfer_batch.sol +385 -0
- package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer.sol +497 -0
- package/contracts/verifiers/impl/anon_nullifier_qurrency_transfer_batch.sol +1001 -0
- package/contracts/verifiers/{verifier_anon_nullifier_transferLocked.sol → impl/anon_nullifier_transfer.sol} +12 -19
- package/contracts/verifiers/{verifier_anon_nullifier_transferLocked_batch.sol → impl/anon_nullifier_transfer_batch.sol} +44 -51
- package/contracts/verifiers/impl/burn.sol +182 -0
- package/contracts/verifiers/impl/burn_batch.sol +238 -0
- package/contracts/verifiers/impl/burn_nullifier.sol +203 -0
- package/contracts/verifiers/impl/burn_nullifier_batch.sol +315 -0
- package/contracts/verifiers/impl/deposit.sol +182 -0
- package/contracts/verifiers/impl/deposit_kyc.sol +189 -0
- package/contracts/verifiers/impl/nf_anon.sol +175 -0
- package/contracts/verifiers/{verifier_nf_anon_nullifier_transferLocked.sol → impl/nf_anon_nullifier_transfer.sol} +6 -13
- package/contracts/verifiers/impl/withdraw.sol +189 -0
- package/contracts/verifiers/impl/withdraw_batch.sol +245 -0
- package/contracts/verifiers/impl/withdraw_nullifier.sol +210 -0
- package/contracts/verifiers/impl/withdraw_nullifier_batch.sol +322 -0
- package/contracts/verifiers/verifier_anon.sol +31 -186
- package/contracts/verifiers/verifier_anon_batch.sol +31 -298
- package/contracts/verifiers/verifier_anon_enc.sol +31 -263
- package/contracts/verifiers/verifier_anon_enc_batch.sol +31 -599
- package/contracts/verifiers/verifier_anon_enc_nullifier.sol +31 -284
- package/contracts/verifiers/verifier_anon_enc_nullifier_batch.sol +33 -676
- package/contracts/verifiers/verifier_anon_enc_nullifier_kyc.sol +31 -291
- package/contracts/verifiers/verifier_anon_enc_nullifier_kyc_batch.sol +33 -683
- package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation.sol +33 -410
- package/contracts/verifiers/verifier_anon_enc_nullifier_non_repudiation_batch.sol +33 -1138
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer.sol +33 -214
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked.sol +33 -221
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transferLocked_batch.sol +33 -389
- package/contracts/verifiers/verifier_anon_nullifier_kyc_transfer_batch.sol +33 -382
- package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer.sol +33 -221
- package/contracts/verifiers/verifier_anon_nullifier_qurrency_transfer_batch.sol +33 -389
- package/contracts/verifiers/verifier_anon_nullifier_transfer.sol +33 -207
- package/contracts/verifiers/verifier_anon_nullifier_transfer_batch.sol +33 -375
- package/contracts/verifiers/verifier_burn.sol +31 -179
- package/contracts/verifiers/verifier_burn_batch.sol +31 -235
- package/contracts/verifiers/verifier_burn_nullifier.sol +31 -200
- package/contracts/verifiers/verifier_burn_nullifier_batch.sol +31 -312
- package/contracts/verifiers/verifier_deposit.sol +31 -179
- package/contracts/verifiers/verifier_deposit_kyc.sol +34 -0
- package/contracts/verifiers/verifier_nf_anon.sol +31 -172
- package/contracts/verifiers/verifier_nf_anon_nullifier_transfer.sol +33 -179
- package/contracts/verifiers/verifier_withdraw.sol +31 -186
- package/contracts/verifiers/verifier_withdraw_batch.sol +31 -242
- package/contracts/verifiers/verifier_withdraw_nullifier.sol +31 -207
- package/contracts/verifiers/verifier_withdraw_nullifier_batch.sol +33 -319
- package/contracts/zeto_anon.sol +77 -231
- package/contracts/zeto_anon_burnable.sol +56 -12
- package/contracts/zeto_anon_enc.sol +93 -190
- package/contracts/zeto_anon_enc_nullifier.sol +249 -195
- package/contracts/zeto_anon_enc_nullifier_kyc.sol +51 -232
- package/contracts/zeto_anon_enc_nullifier_non_repudiation.sol +231 -238
- package/contracts/zeto_anon_nullifier.sol +164 -298
- package/contracts/zeto_anon_nullifier_burnable.sol +68 -18
- package/contracts/zeto_anon_nullifier_kyc.sol +40 -345
- package/contracts/zeto_anon_nullifier_qurrency.sol +218 -360
- package/contracts/zeto_nf_anon.sol +55 -130
- package/contracts/zeto_nf_anon_nullifier.sol +90 -152
- package/hardhat.config.ts +9 -3
- package/ignition/modules/lib/deps.ts +9 -6
- package/ignition/modules/zeto_anon_burnable.ts +8 -7
- package/ignition/modules/zeto_anon_enc_nullifier.ts +33 -1
- package/ignition/modules/zeto_anon_enc_nullifier_kyc.ts +2 -2
- package/ignition/modules/zeto_anon_enc_nullifier_non_repudiation.ts +2 -1
- package/ignition/modules/zeto_anon_nullifier.ts +30 -5
- package/ignition/modules/zeto_anon_nullifier_burnable.ts +52 -17
- package/ignition/modules/zeto_anon_nullifier_kyc.ts +27 -12
- package/ignition/modules/zeto_anon_nullifier_qurrency.ts +2 -1
- package/ignition/modules/zeto_nf_anon_nullifier.ts +16 -12
- package/package.json +4 -2
- package/scripts/deploy_cloneable.ts +19 -10
- package/scripts/deploy_upgradeable.ts +9 -5
- package/scripts/tokens/Zeto_Anon.ts +3 -3
- package/scripts/tokens/Zeto_AnonBurnable.ts +1 -1
- package/scripts/tokens/Zeto_AnonEnc.ts +12 -3
- package/scripts/tokens/Zeto_AnonEncNullifier.ts +7 -3
- package/scripts/tokens/Zeto_AnonEncNullifierKyc.ts +1 -1
- package/scripts/tokens/Zeto_AnonEncNullifierNonRepudiation.ts +3 -1
- package/scripts/tokens/Zeto_AnonNullifier.ts +3 -1
- package/scripts/tokens/Zeto_AnonNullifierBurnable.ts +3 -1
- package/scripts/tokens/Zeto_AnonNullifierKyc.ts +1 -1
- package/scripts/tokens/Zeto_AnonNullifierQurrency.ts +3 -1
- package/scripts/tokens/Zeto_NfAnon.ts +2 -2
- package/scripts/tokens/Zeto_NfAnonNullifier.ts +3 -2
- package/test/factory.ts +46 -73
- package/test/factory_upgradeable.ts +311 -0
- package/test/lib/anon_nullifier_helpers.ts +89 -0
- package/test/lib/anon_zeto_helpers.ts +76 -0
- package/test/lib/deploy.ts +3 -2
- package/test/lib/utils.ts +74 -35
- package/test/test/escrow1.ts +185 -58
- package/test/test/escrow2.ts +200 -107
- package/test/test/qurrency.ts +13 -33
- package/test/usdc-shielding.ts +39 -26
- package/test/utils.ts +144 -21
- package/test/zeto_anon.ts +956 -465
- package/test/zeto_anon_enc.ts +881 -142
- package/test/zeto_anon_enc_nullifier.ts +847 -38
- package/test/zeto_anon_enc_nullifier_kyc.ts +120 -181
- package/test/zeto_anon_enc_nullifier_non_repudiation.ts +77 -46
- package/test/zeto_anon_nullifier.ts +1212 -953
- package/test/zeto_anon_nullifier_kyc.ts +674 -655
- package/test/zeto_anon_nullifier_qurrency.ts +452 -306
- package/test/zeto_nf_anon.ts +737 -137
- package/test/zeto_nf_anon_nullifier.ts +876 -246
- package/contracts/lib/zeto_base.sol +0 -293
- package/contracts/lib/zeto_fungible_withdraw.sol +0 -132
- package/contracts/lib/zeto_fungible_withdraw_nullifier.sol +0 -144
- package/contracts/lib/zeto_nullifier.sol +0 -334
- package/contracts/zkDvP.sol_ +0 -273
- package/test/zkDvP.ts_ +0 -455
- /package/contracts/lib/{common.sol → common/common.sol} +0 -0
|
@@ -15,61 +15,479 @@
|
|
|
15
15
|
// limitations under the License.
|
|
16
16
|
pragma solidity ^0.8.27;
|
|
17
17
|
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import {IGroth16Verifier} from "./interfaces/IZetoVerifier.sol";
|
|
19
|
+
import {IZetoInitializable} from "./interfaces/IZetoInitializable.sol";
|
|
20
|
+
import {IZetoLockableCapability} from "./interfaces/IZetoLockableCapability.sol";
|
|
21
|
+
import {Commonlib} from "./common/common.sol";
|
|
20
22
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
21
|
-
import {
|
|
23
|
+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
24
|
+
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
|
|
25
|
+
import {ZetoLockable} from "./zeto_lockable.sol";
|
|
26
|
+
import {IZetoStorage} from "./interfaces/IZetoStorage.sol";
|
|
22
27
|
|
|
23
28
|
/// @title A sample implementation of a base Zeto fungible token contract
|
|
24
29
|
/// @author Kaleido, Inc.
|
|
25
|
-
/// @dev
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
/// @dev Adds the ERC20-backed deposit/withdraw lifecycle and a multi-input
|
|
31
|
+
/// {transfer} flow on top of the lock-aware {ZetoLockable} base.
|
|
32
|
+
///
|
|
33
|
+
/// The lock-lifecycle plumbing (state, modifiers, external entry
|
|
34
|
+
/// points, views) lives in {ZetoLockable}; this contract supplies
|
|
35
|
+
/// only the two ZK-circuit-shaped hooks
|
|
36
|
+
/// ({_doLockTransition} and {_transferLocked}) that pad
|
|
37
|
+
/// inputs/outputs to the next supported batch size before
|
|
38
|
+
/// {constructPublicInputs}.
|
|
39
|
+
///
|
|
40
|
+
/// Inherits {ReentrancyGuardUpgradeable} so that {deposit} and
|
|
41
|
+
/// {withdraw}, which perform external ERC20 transfers, are
|
|
42
|
+
/// protected from reentrant calls (defense-in-depth on top of the
|
|
43
|
+
/// checks-effects-interactions ordering enforced inside those
|
|
44
|
+
/// functions).
|
|
45
|
+
abstract contract ZetoFungible is ZetoLockable, ReentrancyGuardUpgradeable {
|
|
46
|
+
using SafeERC20 for IERC20;
|
|
31
47
|
|
|
32
|
-
|
|
48
|
+
/// @dev Emitted when {setERC20} successfully binds the backing
|
|
49
|
+
/// ERC20. Indexed by the new token address so subscribers can
|
|
50
|
+
/// filter.
|
|
51
|
+
event ERC20Set(address indexed erc20);
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
/// @dev Thrown when {setERC20} is called with the zero address.
|
|
54
|
+
error ZeroERC20Address();
|
|
35
55
|
|
|
56
|
+
/// @dev Thrown when {setERC20} is called after the backing ERC20
|
|
57
|
+
/// has already been bound. {setERC20} is one-shot: once a
|
|
58
|
+
/// Zeto contract is paired with an ERC20, that pairing is
|
|
59
|
+
/// permanent for the lifetime of the proxy. This protects
|
|
60
|
+
/// depositors from having their backing token rug-pulled out
|
|
61
|
+
/// from under existing UTXOs.
|
|
62
|
+
error ERC20AlreadySet(address current);
|
|
63
|
+
|
|
64
|
+
/// @dev Deposit / withdraw verifiers and ERC20 pairing live in
|
|
65
|
+
/// {ZetoFungibleStorage} (ERC-7201 namespace `zeto.storage.ZetoFungible`).
|
|
66
|
+
|
|
67
|
+
/// @dev Initializer should only ever be called from a derived
|
|
68
|
+
/// contract's own `initializer`-guarded entrypoint, so the
|
|
69
|
+
/// `internal` visibility prevents external poking of the
|
|
70
|
+
/// lifecycle on a partially-initialized proxy.
|
|
36
71
|
function __ZetoFungible_init(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
string calldata name_,
|
|
73
|
+
string calldata symbol_,
|
|
74
|
+
address initialOwner,
|
|
75
|
+
IZetoInitializable.VerifiersInfo calldata verifiers,
|
|
76
|
+
IZetoStorage storage_
|
|
77
|
+
) internal onlyInitializing {
|
|
78
|
+
__ZetoCommon_init(name_, symbol_, initialOwner, verifiers, storage_);
|
|
79
|
+
__ReentrancyGuard_init();
|
|
80
|
+
ZetoFungibleStorage.Layout storage $ = ZetoFungibleStorage.layout();
|
|
81
|
+
$.depositVerifier = verifiers.depositVerifier;
|
|
82
|
+
$.withdrawVerifier = verifiers.withdrawVerifier;
|
|
83
|
+
$.batchWithdrawVerifier = verifiers.batchWithdrawVerifier;
|
|
40
84
|
}
|
|
41
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @dev Bind the ERC20 token that this Zeto contract will interact
|
|
88
|
+
* with for {deposit} and {withdraw}. One-shot: a subsequent
|
|
89
|
+
* call reverts with {ERC20AlreadySet}.
|
|
90
|
+
*
|
|
91
|
+
* Rationale: depositors trust that the asset backing their
|
|
92
|
+
* UTXOs is stable for the lifetime of the contract. Letting
|
|
93
|
+
* the owner swap the backing ERC20 after deposits exist would
|
|
94
|
+
* let them silently change what `withdraw` pays out. Making
|
|
95
|
+
* this one-shot removes that vector entirely while still
|
|
96
|
+
* allowing the operator to wire up the pairing post-deployment.
|
|
97
|
+
*
|
|
98
|
+
* @param erc20 The ERC20 token to be used. Must be non-zero.
|
|
99
|
+
*
|
|
100
|
+
* Emits {ERC20Set}.
|
|
101
|
+
*/
|
|
42
102
|
function setERC20(IERC20 erc20) public onlyOwner {
|
|
43
|
-
|
|
103
|
+
if (address(erc20) == address(0)) {
|
|
104
|
+
revert ZeroERC20Address();
|
|
105
|
+
}
|
|
106
|
+
ZetoFungibleStorage.Layout storage $ = ZetoFungibleStorage.layout();
|
|
107
|
+
if (address($.erc20Token) != address(0)) {
|
|
108
|
+
revert ERC20AlreadySet(address($.erc20Token));
|
|
109
|
+
}
|
|
110
|
+
$.erc20Token = erc20;
|
|
111
|
+
emit ERC20Set(address(erc20));
|
|
44
112
|
}
|
|
45
113
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
114
|
+
/**
|
|
115
|
+
* @dev the main function of the contract, which transfers values from one account (represented by Babyjubjub public keys)
|
|
116
|
+
* to one or more receiver accounts (also represented by Babyjubjub public keys). One of the two nullifiers may be zero
|
|
117
|
+
* if the transaction only needs one UTXO to be spent. Equally one of the two outputs may be zero if the transaction
|
|
118
|
+
* only needs to create one new UTXO.
|
|
119
|
+
*
|
|
120
|
+
* @param inputs Array of nullifiers that are secretly bound to UTXOs to be spent by the transaction.
|
|
121
|
+
* @param outputs Array of new UTXOs to generate, for future transactions to spend.
|
|
122
|
+
* @param proof A zero knowledge proof that the submitter is authorized to spend the inputs, and
|
|
123
|
+
* that the outputs are valid in terms of obeying mass conservation rules.
|
|
124
|
+
*
|
|
125
|
+
* Emits a {UTXOTransfer} event.
|
|
126
|
+
*/
|
|
127
|
+
function transfer(
|
|
128
|
+
uint256[] calldata inputs,
|
|
129
|
+
uint256[] calldata outputs,
|
|
130
|
+
bytes calldata proof,
|
|
131
|
+
bytes calldata data
|
|
50
132
|
) public virtual {
|
|
133
|
+
uint256[] memory lockedOutputs;
|
|
134
|
+
validateTransactionProposal(
|
|
135
|
+
inputs,
|
|
136
|
+
outputs,
|
|
137
|
+
lockedOutputs,
|
|
138
|
+
proof,
|
|
139
|
+
false
|
|
140
|
+
);
|
|
141
|
+
// Check and pad commitments
|
|
142
|
+
(
|
|
143
|
+
uint256[] memory paddedInputs,
|
|
144
|
+
uint256[] memory paddedOutputs
|
|
145
|
+
) = checkAndPadCommitments(inputs, outputs);
|
|
146
|
+
// construct the public inputs for the proof verification
|
|
147
|
+
(
|
|
148
|
+
uint256[] memory publicInputs,
|
|
149
|
+
Commonlib.Proof memory proofStruct
|
|
150
|
+
) = constructPublicInputs(paddedInputs, paddedOutputs, proof, false);
|
|
151
|
+
bool isBatch = (inputs.length > 2 || outputs.length > 2);
|
|
152
|
+
verifyProof(proofStruct, publicInputs, isBatch, false);
|
|
153
|
+
processInputsAndOutputs(paddedInputs, paddedOutputs, false);
|
|
154
|
+
|
|
155
|
+
emitTransferEvent(inputs, outputs, proof, data);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ------------------------------------------------------------------
|
|
159
|
+
// ZetoLockable hook overrides — fungible flavour pads inputs and
|
|
160
|
+
// outputs to the next supported batch size before constructing the
|
|
161
|
+
// public-inputs array.
|
|
162
|
+
// ------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
function _doLockTransition(
|
|
165
|
+
ZetoCreateLockArgs memory args
|
|
166
|
+
) internal override {
|
|
167
|
+
validateTransactionProposal(
|
|
168
|
+
args.inputs,
|
|
169
|
+
args.outputs,
|
|
170
|
+
args.lockedOutputs,
|
|
171
|
+
args.proof,
|
|
172
|
+
false
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// Combine the locked outputs and the regular outputs because the
|
|
176
|
+
// circuits do not differentiate them.
|
|
177
|
+
uint256[] memory allOutputs = new uint256[](
|
|
178
|
+
args.lockedOutputs.length + args.outputs.length
|
|
179
|
+
);
|
|
180
|
+
for (uint256 i = 0; i < args.lockedOutputs.length; i++) {
|
|
181
|
+
allOutputs[i] = args.lockedOutputs[i];
|
|
182
|
+
}
|
|
183
|
+
for (uint256 i = 0; i < args.outputs.length; i++) {
|
|
184
|
+
allOutputs[args.lockedOutputs.length + i] = args.outputs[i];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
(
|
|
188
|
+
uint256[] memory paddedInputs,
|
|
189
|
+
uint256[] memory paddedOutputs
|
|
190
|
+
) = checkAndPadCommitments(args.inputs, allOutputs);
|
|
191
|
+
|
|
192
|
+
(
|
|
193
|
+
uint256[] memory publicInputs,
|
|
194
|
+
Commonlib.Proof memory proofStruct
|
|
195
|
+
) = constructPublicInputs(
|
|
196
|
+
paddedInputs,
|
|
197
|
+
paddedOutputs,
|
|
198
|
+
args.proof,
|
|
199
|
+
false
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
bool isBatch = (paddedInputs.length > 2 ||
|
|
203
|
+
args.outputs.length > 2 ||
|
|
204
|
+
args.lockedOutputs.length > 2);
|
|
205
|
+
verifyProof(proofStruct, publicInputs, isBatch, false);
|
|
206
|
+
|
|
207
|
+
processInputsAndOutputs(paddedInputs, args.outputs, false);
|
|
208
|
+
processLockedOutputs(args.lockedOutputs);
|
|
209
|
+
// The freshly-locked outputs all start under the lock creator
|
|
210
|
+
// as both owner and spender; record that on the per-UTXO
|
|
211
|
+
// projection so {locked} can report it without a reverse lookup.
|
|
212
|
+
_setLockDelegates(args.lockedOutputs, msg.sender);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function _transferLocked(
|
|
216
|
+
bytes32 /* lockId */,
|
|
217
|
+
uint256[] memory lockedInputs,
|
|
218
|
+
uint256[] memory lockedOutputs,
|
|
219
|
+
uint256[] memory outputs,
|
|
220
|
+
bytes memory proof,
|
|
221
|
+
bytes memory /* data */
|
|
222
|
+
) internal override {
|
|
223
|
+
validateTransactionProposal(
|
|
224
|
+
lockedInputs,
|
|
225
|
+
outputs,
|
|
226
|
+
lockedOutputs,
|
|
227
|
+
proof,
|
|
228
|
+
true
|
|
229
|
+
);
|
|
230
|
+
// combine the locked outputs and the outputs, because the
|
|
231
|
+
// circuits do not care about the difference between locked and
|
|
232
|
+
// unlocked outputs
|
|
233
|
+
uint256[] memory allOutputs = new uint256[](
|
|
234
|
+
lockedOutputs.length + outputs.length
|
|
235
|
+
);
|
|
236
|
+
for (uint256 i = 0; i < lockedOutputs.length; i++) {
|
|
237
|
+
allOutputs[i] = lockedOutputs[i];
|
|
238
|
+
}
|
|
239
|
+
for (uint256 i = 0; i < outputs.length; i++) {
|
|
240
|
+
allOutputs[lockedOutputs.length + i] = outputs[i];
|
|
241
|
+
}
|
|
242
|
+
// Check and pad inputs and outputs based on the max size
|
|
243
|
+
(
|
|
244
|
+
uint256[] memory paddedInputs,
|
|
245
|
+
uint256[] memory paddedOutputs
|
|
246
|
+
) = checkAndPadCommitments(lockedInputs, allOutputs);
|
|
247
|
+
// construct the public inputs for the proof verification
|
|
248
|
+
(
|
|
249
|
+
uint256[] memory publicInputs,
|
|
250
|
+
Commonlib.Proof memory proofStruct
|
|
251
|
+
) = constructPublicInputs(paddedInputs, paddedOutputs, proof, true);
|
|
252
|
+
bool isBatch = (lockedInputs.length > 2 || allOutputs.length > 2);
|
|
253
|
+
verifyProof(proofStruct, publicInputs, isBatch, true);
|
|
254
|
+
processInputsAndOutputs(paddedInputs, paddedOutputs, true);
|
|
255
|
+
processLockedOutputs(lockedOutputs);
|
|
256
|
+
// Any newly-locked outputs produced by this spend default to the
|
|
257
|
+
// current spender as their delegate. (When `lockedOutputs` is
|
|
258
|
+
// empty -- the common case -- this is a no-op.)
|
|
259
|
+
_setLockDelegates(lockedOutputs, msg.sender);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @dev Deposit ERC20 tokens into the Zeto contract.
|
|
264
|
+
*
|
|
265
|
+
* @param amount The amount of ERC20 tokens to be deposited.
|
|
266
|
+
* @param outputs The UTXOs to be minted.
|
|
267
|
+
* @param proof The proof of the deposit.
|
|
268
|
+
* @param data Additional data to be passed to the deposit function.
|
|
269
|
+
*
|
|
270
|
+
* Emits a {UTXOMint} event.
|
|
271
|
+
*/
|
|
272
|
+
function deposit(
|
|
273
|
+
uint256 amount,
|
|
274
|
+
uint256[] calldata outputs,
|
|
275
|
+
bytes calldata proof,
|
|
276
|
+
bytes calldata data
|
|
277
|
+
) public nonReentrant {
|
|
278
|
+
// ---- Checks ----
|
|
279
|
+
validateOutputs(outputs);
|
|
280
|
+
|
|
51
281
|
// verifies that the output UTXOs match the claimed value
|
|
52
282
|
// to be deposited
|
|
283
|
+
(
|
|
284
|
+
uint256[] memory publicInputs,
|
|
285
|
+
Commonlib.Proof memory proofStruct
|
|
286
|
+
) = constructPublicInputsForDeposit(amount, outputs, proof);
|
|
287
|
+
if (
|
|
288
|
+
!ZetoFungibleStorage.layout().depositVerifier.verify(
|
|
289
|
+
proofStruct.pA,
|
|
290
|
+
proofStruct.pB,
|
|
291
|
+
proofStruct.pC,
|
|
292
|
+
publicInputs
|
|
293
|
+
)
|
|
294
|
+
) {
|
|
295
|
+
revert InvalidProof();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---- Effects ----
|
|
299
|
+
// Mint the UTXOs (commits the new outputs to storage and emits
|
|
300
|
+
// {UTXOMint}) before the external ERC20 call. This ensures that
|
|
301
|
+
// any reentrant call into this contract triggered by the ERC20
|
|
302
|
+
// transfer observes the new outputs as already-committed and
|
|
303
|
+
// cannot replay them.
|
|
304
|
+
_mint(outputs, data);
|
|
305
|
+
|
|
306
|
+
// ---- Interactions ----
|
|
307
|
+
// SafeERC20 handles non-standard tokens that return no value on
|
|
308
|
+
// success and reverts cleanly when the underlying call fails or
|
|
309
|
+
// returns false.
|
|
310
|
+
ZetoFungibleStorage.layout().erc20Token.safeTransferFrom(
|
|
311
|
+
msg.sender,
|
|
312
|
+
address(this),
|
|
313
|
+
amount
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* @dev Withdraw ERC20 tokens from the Zeto contract.
|
|
319
|
+
*
|
|
320
|
+
* @param amount The amount of ERC20 tokens to be withdrawn.
|
|
321
|
+
* @param inputs The UTXOs to be spent.
|
|
322
|
+
* @param output The UTXO to be minted.
|
|
323
|
+
* @param proof The proof of the withdrawal.
|
|
324
|
+
* @param data Additional data to be passed to the withdrawal
|
|
325
|
+
* function.
|
|
326
|
+
*
|
|
327
|
+
* Emits a {UTXOWithdraw} event.
|
|
328
|
+
*/
|
|
329
|
+
function withdraw(
|
|
330
|
+
uint256 amount,
|
|
331
|
+
uint256[] calldata inputs,
|
|
332
|
+
uint256 output,
|
|
333
|
+
bytes calldata proof,
|
|
334
|
+
bytes calldata data
|
|
335
|
+
) public nonReentrant {
|
|
336
|
+
uint256[] memory outputs = new uint256[](1);
|
|
337
|
+
outputs[0] = output;
|
|
338
|
+
uint256[] memory lockedOutputs;
|
|
339
|
+
|
|
340
|
+
// ---- Checks ----
|
|
341
|
+
validateTransactionProposal(
|
|
342
|
+
inputs,
|
|
343
|
+
outputs,
|
|
344
|
+
lockedOutputs,
|
|
345
|
+
proof,
|
|
346
|
+
false
|
|
347
|
+
);
|
|
348
|
+
// Check and pad inputs and outputs based on the max size
|
|
349
|
+
(
|
|
350
|
+
uint256[] memory paddedInputs,
|
|
351
|
+
uint256[] memory paddedOutputs
|
|
352
|
+
) = checkAndPadCommitments(inputs, outputs);
|
|
353
|
+
(
|
|
354
|
+
uint256[] memory publicInputs,
|
|
355
|
+
Commonlib.Proof memory proofStruct
|
|
356
|
+
) = constructPublicInputsForWithdraw(
|
|
357
|
+
amount,
|
|
358
|
+
paddedInputs,
|
|
359
|
+
output,
|
|
360
|
+
proof
|
|
361
|
+
);
|
|
362
|
+
ZetoFungibleStorage.Layout storage $ = ZetoFungibleStorage.layout();
|
|
363
|
+
IGroth16Verifier verifier = (inputs.length > 2)
|
|
364
|
+
? $.batchWithdrawVerifier
|
|
365
|
+
: $.withdrawVerifier;
|
|
366
|
+
if (
|
|
367
|
+
!verifier.verify(
|
|
368
|
+
proofStruct.pA,
|
|
369
|
+
proofStruct.pB,
|
|
370
|
+
proofStruct.pC,
|
|
371
|
+
publicInputs
|
|
372
|
+
)
|
|
373
|
+
) {
|
|
374
|
+
revert InvalidProof();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ---- Effects ----
|
|
378
|
+
// Mark the input nullifiers as spent and commit the
|
|
379
|
+
// change-output before performing the external ERC20 transfer.
|
|
380
|
+
// Following checks-effects-interactions ensures a callback-style
|
|
381
|
+
// ERC20 cannot re-enter and double-spend the same nullifiers.
|
|
382
|
+
processInputsAndOutputs(paddedInputs, paddedOutputs, false);
|
|
383
|
+
|
|
384
|
+
// ---- Interactions ----
|
|
385
|
+
// SafeERC20 handles non-standard tokens that return no value on
|
|
386
|
+
// success and reverts cleanly when the underlying call fails or
|
|
387
|
+
// returns false.
|
|
388
|
+
ZetoFungibleStorage.layout().erc20Token.safeTransfer(
|
|
389
|
+
msg.sender,
|
|
390
|
+
amount
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
// Emitted after the transfer so that the on-chain event order
|
|
394
|
+
// remains ERC20.Transfer → UTXOWithdraw, matching listeners and
|
|
395
|
+
// tests built before the CEI reorder. Event emission is a pure
|
|
396
|
+
// log and does not affect security; the nullifier state was
|
|
397
|
+
// already committed above.
|
|
398
|
+
emit UTXOWithdraw(amount, inputs, output, msg.sender, data);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function emitTransferEvent(
|
|
402
|
+
uint256[] memory inputs,
|
|
403
|
+
uint256[] memory outputs,
|
|
404
|
+
bytes memory proof,
|
|
405
|
+
bytes memory data
|
|
406
|
+
) internal virtual {
|
|
407
|
+
emit UTXOTransfer(inputs, outputs, msg.sender, data);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// this is a utility function that constructs the public inputs for a proof of a deposit() call.
|
|
411
|
+
// specific implementations of this function may be overridden by each token implementation
|
|
412
|
+
function constructPublicInputsForDeposit(
|
|
413
|
+
uint256 amount,
|
|
414
|
+
uint256[] memory outputs,
|
|
415
|
+
bytes memory proof
|
|
416
|
+
) public virtual returns (uint256[] memory, Commonlib.Proof memory) {
|
|
417
|
+
Commonlib.Proof memory proofStruct = abi.decode(
|
|
418
|
+
proof,
|
|
419
|
+
(Commonlib.Proof)
|
|
420
|
+
);
|
|
53
421
|
// construct the public inputs
|
|
54
|
-
uint256[
|
|
422
|
+
uint256[] memory extra = extraInputsForDeposit();
|
|
423
|
+
uint256[] memory publicInputs = new uint256[](3 + extra.length);
|
|
55
424
|
publicInputs[0] = amount;
|
|
56
425
|
publicInputs[1] = outputs[0];
|
|
57
426
|
publicInputs[2] = outputs[1];
|
|
427
|
+
for (uint256 i = 0; i < extra.length; i++) {
|
|
428
|
+
publicInputs[3 + i] = extra[i];
|
|
429
|
+
}
|
|
58
430
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
);
|
|
431
|
+
return (publicInputs, proofStruct);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function extraInputsForDeposit()
|
|
435
|
+
internal
|
|
436
|
+
view
|
|
437
|
+
virtual
|
|
438
|
+
returns (uint256[] memory)
|
|
439
|
+
{
|
|
440
|
+
return new uint256[](0);
|
|
441
|
+
}
|
|
69
442
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
443
|
+
// this is a utility function that constructs the public inputs for a proof of a withdraw() call.
|
|
444
|
+
// specific implementations of this function may be overridden by each token implementation
|
|
445
|
+
function constructPublicInputsForWithdraw(
|
|
446
|
+
uint256 amount,
|
|
447
|
+
uint256[] memory inputs,
|
|
448
|
+
uint256 output,
|
|
449
|
+
bytes memory proof
|
|
450
|
+
) internal virtual returns (uint256[] memory, Commonlib.Proof memory) {
|
|
451
|
+
Commonlib.Proof memory proofStruct = abi.decode(
|
|
452
|
+
proof,
|
|
453
|
+
(Commonlib.Proof)
|
|
73
454
|
);
|
|
455
|
+
uint256 size = (inputs.length + 1 + 1); // inputs, output, and amount
|
|
456
|
+
|
|
457
|
+
uint256[] memory publicInputs = new uint256[](size);
|
|
458
|
+
uint256 piIndex = 0;
|
|
459
|
+
|
|
460
|
+
// copy output amount
|
|
461
|
+
publicInputs[piIndex++] = amount;
|
|
462
|
+
|
|
463
|
+
// copy input commitments
|
|
464
|
+
for (uint256 i = 0; i < inputs.length; i++) {
|
|
465
|
+
publicInputs[piIndex++] = inputs[i];
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// copy output commitment
|
|
469
|
+
publicInputs[piIndex++] = output;
|
|
470
|
+
|
|
471
|
+
return (publicInputs, proofStruct);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/// @dev ERC-7201 (`erc7201:zeto.storage.ZetoFungible`): deposit/withdraw verifiers
|
|
476
|
+
/// and ERC20 pairing for this abstract contract.
|
|
477
|
+
library ZetoFungibleStorage {
|
|
478
|
+
struct Layout {
|
|
479
|
+
IGroth16Verifier depositVerifier;
|
|
480
|
+
IGroth16Verifier withdrawVerifier;
|
|
481
|
+
IGroth16Verifier batchWithdrawVerifier;
|
|
482
|
+
IERC20 erc20Token;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
bytes32 private constant STORAGE_LOCATION =
|
|
486
|
+
0x502fd05a4212f6b786ff372127438db29bf1c676e6268e59ca1feb2fae998d00;
|
|
487
|
+
|
|
488
|
+
function layout() internal pure returns (Layout storage $) {
|
|
489
|
+
assembly {
|
|
490
|
+
$.slot := STORAGE_LOCATION
|
|
491
|
+
}
|
|
74
492
|
}
|
|
75
493
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright © 2024 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 {IZetoInitializable} from "./interfaces/IZetoInitializable.sol";
|
|
19
|
+
import {ZetoFungible} from "./zeto_fungible.sol";
|
|
20
|
+
import {IZetoStorage} from "./interfaces/IZetoStorage.sol";
|
|
21
|
+
import {BaseStorage} from "./storage/base.sol";
|
|
22
|
+
|
|
23
|
+
/// @title A sample base implementation of a Zeto based fungible token contract
|
|
24
|
+
/// WITHOUT nullifiers. Each UTXO's spending status is explicitly tracked
|
|
25
|
+
/// in the {BaseStorage} ledger.
|
|
26
|
+
/// @author Kaleido, Inc.
|
|
27
|
+
/// @dev Sibling of {ZetoFungibleNullifier}. The two differ only in the storage
|
|
28
|
+
/// backend wired into {ZetoFungible}: this contract uses {BaseStorage},
|
|
29
|
+
/// which records `(utxo => UTXOStatus)` for both regular and locked UTXOs;
|
|
30
|
+
/// the nullifier sibling uses {NullifierStorage}, which records spent
|
|
31
|
+
/// nullifiers in a Sparse Merkle Tree.
|
|
32
|
+
///
|
|
33
|
+
/// The locking lifecycle (createLock / updateLock / delegateLock /
|
|
34
|
+
/// spendLock / cancelLock) is inherited unchanged from {ZetoFungible}
|
|
35
|
+
/// and is therefore identical across both backends. The only contract
|
|
36
|
+
/// that differs between the two siblings is the leaf token (e.g.
|
|
37
|
+
/// Zeto_Anon vs Zeto_AnonNullifier), which supplies the
|
|
38
|
+
/// circuit-specific `constructPublicInputs` implementation.
|
|
39
|
+
///
|
|
40
|
+
/// Future non-nullifier-only state belongs in {ZetoFungibleBaseStorage}
|
|
41
|
+
/// below — append fields there on upgrade instead of `__gap`.
|
|
42
|
+
abstract contract ZetoFungibleBase is ZetoFungible {
|
|
43
|
+
function __ZetoFungibleBase_init(
|
|
44
|
+
string calldata name_,
|
|
45
|
+
string calldata symbol_,
|
|
46
|
+
address initialOwner,
|
|
47
|
+
IZetoInitializable.VerifiersInfo calldata verifiers
|
|
48
|
+
) internal onlyInitializing {
|
|
49
|
+
IZetoStorage storage_ = new BaseStorage();
|
|
50
|
+
__ZetoFungible_init(name_, symbol_, initialOwner, verifiers, storage_);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// @dev ERC-7201 (`erc7201:zeto.storage.ZetoFungibleBase`): reserved for
|
|
55
|
+
/// non-nullifier fungible leaf extensions.
|
|
56
|
+
library ZetoFungibleBaseStorage {
|
|
57
|
+
struct Layout {
|
|
58
|
+
uint256 __reserved;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
bytes32 private constant STORAGE_LOCATION =
|
|
62
|
+
0x06a87037c58e5288ef9ee2ed0d7efb7c68b30748ac0ed4d5eb8700eb60966f00;
|
|
63
|
+
|
|
64
|
+
function layout() internal pure returns (Layout storage $) {
|
|
65
|
+
assembly {
|
|
66
|
+
$.slot := STORAGE_LOCATION
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|