-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds create2 deploy methods + refactoring typescript interfaces (#218)
Co-authored-by: drewstone <drewstone329@gmail.com>
- Loading branch information
1 parent
1295a35
commit 56ad8f5
Showing
20 changed files
with
2,911 additions
and
2,322 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
); | ||
} | ||
} |
Oops, something went wrong.