Skip to content

Commit

Permalink
Adds create2 deploy methods + refactoring typescript interfaces (#218)
Browse files Browse the repository at this point in the history
Co-authored-by: drewstone <drewstone329@gmail.com>
  • Loading branch information
semaraugusto and drewstone authored Dec 26, 2022
1 parent 1295a35 commit 56ad8f5
Show file tree
Hide file tree
Showing 20 changed files with 2,911 additions and 2,322 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"build": "yarn compile && yarn build:packages",
"setup": "yarn compile && yarn build:circuits && yarn build:ptau",
"test": "yarn workspace @webb-tools/contracts run test",
"fast": "yarn workspace @webb-tools/contracts run fast",
"build:circuits": "./scripts/bash/build_circuits.sh",
"build:packages": "lerna run build",
"build:ptau": "./scripts/bash/generate_phase1_ptau.sh",
Expand Down Expand Up @@ -68,10 +69,10 @@
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"@webb-tools/sdk-core": "0.1.4-113",
"@webb-tools/semaphore": "0.0.1-4",
"@webb-tools/semaphore": "0.0.1-5",
"@webb-tools/semaphore-group": "0.0.1-4",
"@webb-tools/semaphore-identity": "0.0.1-3",
"@webb-tools/semaphore-proof": "0.0.1-3",
"@webb-tools/semaphore-proof": "0.0.1-5",
"@webb-tools/test-utils": "0.1.4-113",
"babel-plugin-styled-components": "^2.0.7",
"bn.js": "4.11.6",
Expand Down
2 changes: 1 addition & 1 deletion packages/anchors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@webb-tools/contracts": "^0.5.1",
"@webb-tools/interfaces": "^0.5.1",
"@webb-tools/sdk-core": "0.1.4-113",
"@webb-tools/semaphore": "0.0.1-4",
"@webb-tools/semaphore": "0.0.1-5",
"@webb-tools/utils": "^0.5.1",
"ethers": "5.7.0"
},
Expand Down
47 changes: 47 additions & 0 deletions packages/anchors/src/ChainalysisVAnchor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,55 @@ import { ZkComponents } from '@webb-tools/utils';
import { BigNumberish, ethers, BigNumber } from 'ethers';
import { VAnchorEncodeInputs__factory, ChainalysisVAnchor__factory } from '@webb-tools/contracts';
import VAnchor from './VAnchor';
import { Deployer } from './Deployer';

export class ChainalysisVAnchor extends VAnchor {
public static async create2VAnchor(
deployer: Deployer,
saltHex: string,
verifier: string,
levels: BigNumberish,
hasher: string,
handler: string,
token: string,
maxEdges: number,
smallCircuitZkComponents: ZkComponents,
largeCircuitZkComponents: ZkComponents,
signer: ethers.Signer
) {
const { contract: libraryContract } = await deployer.deploy(
VAnchorEncodeInputs__factory,
saltHex,
signer
);

let libraryAddresses = {
['contracts/libs/VAnchorEncodeInputs.sol:VAnchorEncodeInputs']: libraryContract.address,
};

const argTypes = ['address', 'uint32', 'address', 'address', 'address', 'uint8'];
const args = [verifier, levels, hasher, handler, token, maxEdges];
const { contract: vanchor, receipt } = await deployer.deploy(
ChainalysisVAnchor__factory,
saltHex,
signer,
libraryAddresses,
argTypes,
args
);
const createdVAnchor = new VAnchor(
vanchor,
signer,
BigNumber.from(levels).toNumber(),
maxEdges,
smallCircuitZkComponents,
largeCircuitZkComponents
);
createdVAnchor.latestSyncedBlock = receipt.blockNumber!;
createdVAnchor.token = token;
return createdVAnchor;
}

public static async createVAnchor(
verifier: string,
levels: BigNumberish,
Expand Down
270 changes: 270 additions & 0 deletions packages/anchors/src/Common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
import { BigNumber, BigNumberish, ContractTransaction, ethers } from 'ethers';
import {
VAnchor as VAnchorContract,
VAnchor__factory,
ChainalysisVAnchor as ChainalysisVAnchorContract,
DeterministicDeployFactory as DeterministicDeployFactoryContract,
IdentityVAnchor as IdentityVAnchorContract,
VAnchorForest as VAnchorForestContract,
VAnchorEncodeInputs__factory,
TokenWrapper__factory,
} from '@webb-tools/contracts';
import {
toHex,
Keypair,
toFixedHex,
Utxo,
MerkleTree,
median,
mean,
max,
min,
randomBN,
CircomProvingManager,
ProvingManagerSetupInput,
MerkleProof,
UtxoGenInput,
CircomUtxo,
FIELD_SIZE,
LeafIdentifier,
} from '@webb-tools/sdk-core';
import { hexToU8a, u8aToHex, getChainIdType, ZkComponents } from '@webb-tools/utils';

const zeroAddress = '0x0000000000000000000000000000000000000000';
function checkNativeAddress(tokenAddress: string): boolean {
if (tokenAddress === zeroAddress || tokenAddress === '0') {
return true;
}
return false;
}
type WebbContracts =
| VAnchorContract
| ChainalysisVAnchorContract
| IdentityVAnchorContract
| VAnchorForestContract;

export class WebbBridge {
signer: ethers.Signer;
contract: WebbContracts;

constructor(contract: WebbContracts, signer: ethers.Signer) {
this.contract = contract;
this.signer = signer;
}

public static async generateUTXO(input: UtxoGenInput): Promise<Utxo> {
return CircomUtxo.generateUtxo(input);
}

public static createRootsBytes(rootArray: string[]) {
let rootsBytes = '0x';
for (let i = 0; i < rootArray.length; i++) {
rootsBytes += toFixedHex(rootArray[i]).substr(2);
}
return rootsBytes; // root byte string (32 * array.length bytes)
}

getAddress(): string {
return this.contract.address;
}

// Convert a hex string to a byte array
public static hexStringToByte(str: string) {
if (!str) {
return new Uint8Array();
}

var a = [];
for (var i = 0, len = str.length; i < len; i += 2) {
a.push(parseInt(str.substr(i, 2), 16));
}

return new Uint8Array(a);
}

public async setHandler(handlerAddress: string) {
const tx = await this.contract.setHandler(
handlerAddress,
BigNumber.from(await this.contract.getProposalNonce()).add(1)
);
await tx.wait();
}

public async setSigner(newSigner: ethers.Signer) {
const currentChainId = await this.signer.getChainId();
const newChainId = await newSigner.getChainId();

if (currentChainId === newChainId) {
this.signer = newSigner;
this.contract = this.contract.connect(newSigner);
return true;
}
return false;
}
public async createResourceId(): Promise<string> {
return toHex(
this.contract.address + toHex(getChainIdType(await this.signer.getChainId()), 6).substr(2),
32
);
}
public async getMinWithdrawalLimitProposalData(
_minimalWithdrawalAmount: string
): Promise<string> {
const resourceID = await this.createResourceId();
const functionSig = ethers.utils
.keccak256(ethers.utils.toUtf8Bytes('configureMinimalWithdrawalLimit(uint256,uint32)'))
.slice(0, 10)
.padEnd(10, '0');
const nonce = Number(await this.contract.getProposalNonce()) + 1;
return (
'0x' +
toHex(resourceID, 32).substr(2) +
functionSig.slice(2) +
toHex(nonce, 4).substr(2) +
toFixedHex(_minimalWithdrawalAmount).substr(2)
);
}

public async getMaxDepositLimitProposalData(_maximumDepositAmount: string): Promise<string> {
const resourceID = await this.createResourceId();
const functionSig = ethers.utils
.keccak256(ethers.utils.toUtf8Bytes('configureMaximumDepositLimit(uint256,uint32)'))
.slice(0, 10)
.padEnd(10, '0');
const nonce = Number(await this.contract.getProposalNonce()) + 1;
return (
'0x' +
toHex(resourceID, 32).substr(2) +
functionSig.slice(2) +
toHex(nonce, 4).substr(2) +
toFixedHex(_maximumDepositAmount).substr(2)
);
}

// Proposal data is used to update linkedAnchors via bridge proposals
// on other chains with this anchor's state
public async genProposalData(
resourceID: string,
merkleRoot: string,
leafIndex: number
): Promise<string> {
// If no leaf index passed in, set it to the most recent one.
const chainID = getChainIdType(await this.signer.getChainId());
const functionSig = ethers.utils
.keccak256(ethers.utils.toUtf8Bytes('updateEdge(uint256,uint32,bytes32)'))
.slice(0, 10)
.padEnd(10, '0');

const srcContract = this.contract.address;
const srcResourceId =
'0x' +
toHex(0, 6).substring(2) +
toHex(srcContract, 20).substr(2) +
toHex(chainID, 6).substr(2);
return (
'0x' +
toHex(resourceID, 32).substr(2) +
functionSig.slice(2) +
toHex(leafIndex, 4).substr(2) +
toHex(merkleRoot, 32).substr(2) +
toHex(srcResourceId, 32).substr(2)
);
}

public getExtAmount(inputs: Utxo[], outputs: Utxo[], fee: BigNumberish) {
return BigNumber.from(fee)
.add(outputs.reduce((sum, x) => sum.add(x.amount), BigNumber.from(0)))
.sub(inputs.reduce((sum, x) => sum.add(x.amount), BigNumber.from(0)));
}
public async getWrapUnwrapOptions(extAmount, wrapUnwrapToken) {
let options = {};
if (extAmount.gt(0) && checkNativeAddress(wrapUnwrapToken)) {
let tokenWrapper = TokenWrapper__factory.connect(await this.contract.token(), this.signer);
let valueToSend = await tokenWrapper.getAmountToWrap(extAmount);

options = {
value: valueToSend.toHexString(),
};
} else {
options = {};
}
return options;
}
public async encodeSolidityProof(fullProof: any, calldata: any): Promise<String> {
const proof = JSON.parse('[' + calldata + ']');
const pi_a = proof[0];
const pi_b = proof[1];
const pi_c = proof[2];

const proofEncoded = [
pi_a[0],
pi_a[1],
pi_b[0][0],
pi_b[0][1],
pi_b[1][0],
pi_b[1][1],
pi_c[0],
pi_c[1],
]
.map((elt) => elt.substr(2))
.join('');

return proofEncoded;
}

public async padUtxos(utxos: Utxo[], maxLen: number): Promise<Utxo[]> {
const evmId = await this.signer.getChainId();
const chainId = getChainIdType(evmId);
const randomKeypair = new Keypair();

while (utxos.length !== 2 && utxos.length < maxLen) {
utxos.push(
await CircomUtxo.generateUtxo({
curve: 'Bn254',
backend: 'Circom',
chainId: chainId.toString(),
originChainId: chainId.toString(),
amount: '0',
blinding: hexToU8a(randomBN(31).toHexString()),
keypair: randomKeypair,
})
);
}
if (utxos.length !== 2 && utxos.length !== maxLen) {
throw new Error('Invalid utxo length');
}
return utxos;
}

// Proposal data is used to update linkedAnchors via bridge proposals
// on other chains with this anchor's state

public async getHandler(): Promise<string> {
return this.contract.handler();
}

public validateInputs(inputs: Utxo[]): void {
inputs.map((utxo) => {
if (utxo.originChainId === undefined) {
throw new Error('Input Utxo does not have a configured originChainId');
}
});
}

public async getHandlerProposalData(newHandler: string): Promise<string> {
const resourceID = await this.createResourceId();
const functionSig = ethers.utils
.keccak256(ethers.utils.toUtf8Bytes('setHandler(address,uint32)'))
.slice(0, 10)
.padEnd(10, '0');
const nonce = Number(await this.contract.getProposalNonce()) + 1;

return (
'0x' +
toHex(resourceID, 32).substr(2) +
functionSig.slice(2) +
toHex(nonce, 4).substr(2) +
toHex(newHandler, 20).substr(2)
);
}
}
Loading

0 comments on commit 56ad8f5

Please sign in to comment.