Skip to content

Commit 56ad8f5

Browse files
Adds create2 deploy methods + refactoring typescript interfaces (#218)
Co-authored-by: drewstone <[email protected]>
1 parent 1295a35 commit 56ad8f5

20 files changed

+2911
-2322
lines changed

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"build": "yarn compile && yarn build:packages",
1313
"setup": "yarn compile && yarn build:circuits && yarn build:ptau",
1414
"test": "yarn workspace @webb-tools/contracts run test",
15+
"fast": "yarn workspace @webb-tools/contracts run fast",
1516
"build:circuits": "./scripts/bash/build_circuits.sh",
1617
"build:packages": "lerna run build",
1718
"build:ptau": "./scripts/bash/generate_phase1_ptau.sh",
@@ -68,10 +69,10 @@
6869
"@types/chai": "^4.3.0",
6970
"@types/mocha": "^9.0.0",
7071
"@webb-tools/sdk-core": "0.1.4-113",
71-
"@webb-tools/semaphore": "0.0.1-4",
72+
"@webb-tools/semaphore": "0.0.1-5",
7273
"@webb-tools/semaphore-group": "0.0.1-4",
7374
"@webb-tools/semaphore-identity": "0.0.1-3",
74-
"@webb-tools/semaphore-proof": "0.0.1-3",
75+
"@webb-tools/semaphore-proof": "0.0.1-5",
7576
"@webb-tools/test-utils": "0.1.4-113",
7677
"babel-plugin-styled-components": "^2.0.7",
7778
"bn.js": "4.11.6",

packages/anchors/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"@webb-tools/contracts": "^0.5.1",
1313
"@webb-tools/interfaces": "^0.5.1",
1414
"@webb-tools/sdk-core": "0.1.4-113",
15-
"@webb-tools/semaphore": "0.0.1-4",
15+
"@webb-tools/semaphore": "0.0.1-5",
1616
"@webb-tools/utils": "^0.5.1",
1717
"ethers": "5.7.0"
1818
},

packages/anchors/src/ChainalysisVAnchor.ts

+47
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,55 @@ import { ZkComponents } from '@webb-tools/utils';
22
import { BigNumberish, ethers, BigNumber } from 'ethers';
33
import { VAnchorEncodeInputs__factory, ChainalysisVAnchor__factory } from '@webb-tools/contracts';
44
import VAnchor from './VAnchor';
5+
import { Deployer } from './Deployer';
56

67
export class ChainalysisVAnchor extends VAnchor {
8+
public static async create2VAnchor(
9+
deployer: Deployer,
10+
saltHex: string,
11+
verifier: string,
12+
levels: BigNumberish,
13+
hasher: string,
14+
handler: string,
15+
token: string,
16+
maxEdges: number,
17+
smallCircuitZkComponents: ZkComponents,
18+
largeCircuitZkComponents: ZkComponents,
19+
signer: ethers.Signer
20+
) {
21+
const { contract: libraryContract } = await deployer.deploy(
22+
VAnchorEncodeInputs__factory,
23+
saltHex,
24+
signer
25+
);
26+
27+
let libraryAddresses = {
28+
['contracts/libs/VAnchorEncodeInputs.sol:VAnchorEncodeInputs']: libraryContract.address,
29+
};
30+
31+
const argTypes = ['address', 'uint32', 'address', 'address', 'address', 'uint8'];
32+
const args = [verifier, levels, hasher, handler, token, maxEdges];
33+
const { contract: vanchor, receipt } = await deployer.deploy(
34+
ChainalysisVAnchor__factory,
35+
saltHex,
36+
signer,
37+
libraryAddresses,
38+
argTypes,
39+
args
40+
);
41+
const createdVAnchor = new VAnchor(
42+
vanchor,
43+
signer,
44+
BigNumber.from(levels).toNumber(),
45+
maxEdges,
46+
smallCircuitZkComponents,
47+
largeCircuitZkComponents
48+
);
49+
createdVAnchor.latestSyncedBlock = receipt.blockNumber!;
50+
createdVAnchor.token = token;
51+
return createdVAnchor;
52+
}
53+
754
public static async createVAnchor(
855
verifier: string,
956
levels: BigNumberish,

packages/anchors/src/Common.ts

+270
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
import { BigNumber, BigNumberish, ContractTransaction, ethers } from 'ethers';
2+
import {
3+
VAnchor as VAnchorContract,
4+
VAnchor__factory,
5+
ChainalysisVAnchor as ChainalysisVAnchorContract,
6+
DeterministicDeployFactory as DeterministicDeployFactoryContract,
7+
IdentityVAnchor as IdentityVAnchorContract,
8+
VAnchorForest as VAnchorForestContract,
9+
VAnchorEncodeInputs__factory,
10+
TokenWrapper__factory,
11+
} from '@webb-tools/contracts';
12+
import {
13+
toHex,
14+
Keypair,
15+
toFixedHex,
16+
Utxo,
17+
MerkleTree,
18+
median,
19+
mean,
20+
max,
21+
min,
22+
randomBN,
23+
CircomProvingManager,
24+
ProvingManagerSetupInput,
25+
MerkleProof,
26+
UtxoGenInput,
27+
CircomUtxo,
28+
FIELD_SIZE,
29+
LeafIdentifier,
30+
} from '@webb-tools/sdk-core';
31+
import { hexToU8a, u8aToHex, getChainIdType, ZkComponents } from '@webb-tools/utils';
32+
33+
const zeroAddress = '0x0000000000000000000000000000000000000000';
34+
function checkNativeAddress(tokenAddress: string): boolean {
35+
if (tokenAddress === zeroAddress || tokenAddress === '0') {
36+
return true;
37+
}
38+
return false;
39+
}
40+
type WebbContracts =
41+
| VAnchorContract
42+
| ChainalysisVAnchorContract
43+
| IdentityVAnchorContract
44+
| VAnchorForestContract;
45+
46+
export class WebbBridge {
47+
signer: ethers.Signer;
48+
contract: WebbContracts;
49+
50+
constructor(contract: WebbContracts, signer: ethers.Signer) {
51+
this.contract = contract;
52+
this.signer = signer;
53+
}
54+
55+
public static async generateUTXO(input: UtxoGenInput): Promise<Utxo> {
56+
return CircomUtxo.generateUtxo(input);
57+
}
58+
59+
public static createRootsBytes(rootArray: string[]) {
60+
let rootsBytes = '0x';
61+
for (let i = 0; i < rootArray.length; i++) {
62+
rootsBytes += toFixedHex(rootArray[i]).substr(2);
63+
}
64+
return rootsBytes; // root byte string (32 * array.length bytes)
65+
}
66+
67+
getAddress(): string {
68+
return this.contract.address;
69+
}
70+
71+
// Convert a hex string to a byte array
72+
public static hexStringToByte(str: string) {
73+
if (!str) {
74+
return new Uint8Array();
75+
}
76+
77+
var a = [];
78+
for (var i = 0, len = str.length; i < len; i += 2) {
79+
a.push(parseInt(str.substr(i, 2), 16));
80+
}
81+
82+
return new Uint8Array(a);
83+
}
84+
85+
public async setHandler(handlerAddress: string) {
86+
const tx = await this.contract.setHandler(
87+
handlerAddress,
88+
BigNumber.from(await this.contract.getProposalNonce()).add(1)
89+
);
90+
await tx.wait();
91+
}
92+
93+
public async setSigner(newSigner: ethers.Signer) {
94+
const currentChainId = await this.signer.getChainId();
95+
const newChainId = await newSigner.getChainId();
96+
97+
if (currentChainId === newChainId) {
98+
this.signer = newSigner;
99+
this.contract = this.contract.connect(newSigner);
100+
return true;
101+
}
102+
return false;
103+
}
104+
public async createResourceId(): Promise<string> {
105+
return toHex(
106+
this.contract.address + toHex(getChainIdType(await this.signer.getChainId()), 6).substr(2),
107+
32
108+
);
109+
}
110+
public async getMinWithdrawalLimitProposalData(
111+
_minimalWithdrawalAmount: string
112+
): Promise<string> {
113+
const resourceID = await this.createResourceId();
114+
const functionSig = ethers.utils
115+
.keccak256(ethers.utils.toUtf8Bytes('configureMinimalWithdrawalLimit(uint256,uint32)'))
116+
.slice(0, 10)
117+
.padEnd(10, '0');
118+
const nonce = Number(await this.contract.getProposalNonce()) + 1;
119+
return (
120+
'0x' +
121+
toHex(resourceID, 32).substr(2) +
122+
functionSig.slice(2) +
123+
toHex(nonce, 4).substr(2) +
124+
toFixedHex(_minimalWithdrawalAmount).substr(2)
125+
);
126+
}
127+
128+
public async getMaxDepositLimitProposalData(_maximumDepositAmount: string): Promise<string> {
129+
const resourceID = await this.createResourceId();
130+
const functionSig = ethers.utils
131+
.keccak256(ethers.utils.toUtf8Bytes('configureMaximumDepositLimit(uint256,uint32)'))
132+
.slice(0, 10)
133+
.padEnd(10, '0');
134+
const nonce = Number(await this.contract.getProposalNonce()) + 1;
135+
return (
136+
'0x' +
137+
toHex(resourceID, 32).substr(2) +
138+
functionSig.slice(2) +
139+
toHex(nonce, 4).substr(2) +
140+
toFixedHex(_maximumDepositAmount).substr(2)
141+
);
142+
}
143+
144+
// Proposal data is used to update linkedAnchors via bridge proposals
145+
// on other chains with this anchor's state
146+
public async genProposalData(
147+
resourceID: string,
148+
merkleRoot: string,
149+
leafIndex: number
150+
): Promise<string> {
151+
// If no leaf index passed in, set it to the most recent one.
152+
const chainID = getChainIdType(await this.signer.getChainId());
153+
const functionSig = ethers.utils
154+
.keccak256(ethers.utils.toUtf8Bytes('updateEdge(uint256,uint32,bytes32)'))
155+
.slice(0, 10)
156+
.padEnd(10, '0');
157+
158+
const srcContract = this.contract.address;
159+
const srcResourceId =
160+
'0x' +
161+
toHex(0, 6).substring(2) +
162+
toHex(srcContract, 20).substr(2) +
163+
toHex(chainID, 6).substr(2);
164+
return (
165+
'0x' +
166+
toHex(resourceID, 32).substr(2) +
167+
functionSig.slice(2) +
168+
toHex(leafIndex, 4).substr(2) +
169+
toHex(merkleRoot, 32).substr(2) +
170+
toHex(srcResourceId, 32).substr(2)
171+
);
172+
}
173+
174+
public getExtAmount(inputs: Utxo[], outputs: Utxo[], fee: BigNumberish) {
175+
return BigNumber.from(fee)
176+
.add(outputs.reduce((sum, x) => sum.add(x.amount), BigNumber.from(0)))
177+
.sub(inputs.reduce((sum, x) => sum.add(x.amount), BigNumber.from(0)));
178+
}
179+
public async getWrapUnwrapOptions(extAmount, wrapUnwrapToken) {
180+
let options = {};
181+
if (extAmount.gt(0) && checkNativeAddress(wrapUnwrapToken)) {
182+
let tokenWrapper = TokenWrapper__factory.connect(await this.contract.token(), this.signer);
183+
let valueToSend = await tokenWrapper.getAmountToWrap(extAmount);
184+
185+
options = {
186+
value: valueToSend.toHexString(),
187+
};
188+
} else {
189+
options = {};
190+
}
191+
return options;
192+
}
193+
public async encodeSolidityProof(fullProof: any, calldata: any): Promise<String> {
194+
const proof = JSON.parse('[' + calldata + ']');
195+
const pi_a = proof[0];
196+
const pi_b = proof[1];
197+
const pi_c = proof[2];
198+
199+
const proofEncoded = [
200+
pi_a[0],
201+
pi_a[1],
202+
pi_b[0][0],
203+
pi_b[0][1],
204+
pi_b[1][0],
205+
pi_b[1][1],
206+
pi_c[0],
207+
pi_c[1],
208+
]
209+
.map((elt) => elt.substr(2))
210+
.join('');
211+
212+
return proofEncoded;
213+
}
214+
215+
public async padUtxos(utxos: Utxo[], maxLen: number): Promise<Utxo[]> {
216+
const evmId = await this.signer.getChainId();
217+
const chainId = getChainIdType(evmId);
218+
const randomKeypair = new Keypair();
219+
220+
while (utxos.length !== 2 && utxos.length < maxLen) {
221+
utxos.push(
222+
await CircomUtxo.generateUtxo({
223+
curve: 'Bn254',
224+
backend: 'Circom',
225+
chainId: chainId.toString(),
226+
originChainId: chainId.toString(),
227+
amount: '0',
228+
blinding: hexToU8a(randomBN(31).toHexString()),
229+
keypair: randomKeypair,
230+
})
231+
);
232+
}
233+
if (utxos.length !== 2 && utxos.length !== maxLen) {
234+
throw new Error('Invalid utxo length');
235+
}
236+
return utxos;
237+
}
238+
239+
// Proposal data is used to update linkedAnchors via bridge proposals
240+
// on other chains with this anchor's state
241+
242+
public async getHandler(): Promise<string> {
243+
return this.contract.handler();
244+
}
245+
246+
public validateInputs(inputs: Utxo[]): void {
247+
inputs.map((utxo) => {
248+
if (utxo.originChainId === undefined) {
249+
throw new Error('Input Utxo does not have a configured originChainId');
250+
}
251+
});
252+
}
253+
254+
public async getHandlerProposalData(newHandler: string): Promise<string> {
255+
const resourceID = await this.createResourceId();
256+
const functionSig = ethers.utils
257+
.keccak256(ethers.utils.toUtf8Bytes('setHandler(address,uint32)'))
258+
.slice(0, 10)
259+
.padEnd(10, '0');
260+
const nonce = Number(await this.contract.getProposalNonce()) + 1;
261+
262+
return (
263+
'0x' +
264+
toHex(resourceID, 32).substr(2) +
265+
functionSig.slice(2) +
266+
toHex(nonce, 4).substr(2) +
267+
toHex(newHandler, 20).substr(2)
268+
);
269+
}
270+
}

0 commit comments

Comments
 (0)