diff --git a/.gitignore b/.gitignore index d502512..52d1bae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /node_modules /package-lock.json +/artifacts +/cache \ No newline at end of file diff --git a/contracts/assist/Gas.sol b/contracts/assist/Gas.sol index f2ad0a0..4870eb4 100644 --- a/contracts/assist/Gas.sol +++ b/contracts/assist/Gas.sol @@ -1,13 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; + import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/utils/Counters.sol"; -contract Gas is ERC20{ - constructor() ERC20("Test Token", "TestToken") {} - function mint(uint256 amount) public { - _mint(msg.sender, amount); - } - function getBalance() public view returns (uint256) { - return balanceOf(msg.sender); - } + +contract Gas is ERC20 { + constructor() ERC20("Test Token", "TestToken") {} + + function mint(uint256 amount) public { + _mint(msg.sender, amount); + } + + function getBalance() public view returns (uint256) { + return balanceOf(msg.sender); + } } diff --git a/contracts/assist/Token.sol b/contracts/assist/Token.sol index 1469d87..cc92c64 100644 --- a/contracts/assist/Token.sol +++ b/contracts/assist/Token.sol @@ -1,13 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; + import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/utils/Counters.sol"; -contract Token is ERC20{ - constructor() ERC20("Test Token", "TestToken") {} - function mint(uint256 amount) public { - _mint(msg.sender, amount); - } - function getBalance() public view returns (uint256) { - return balanceOf(msg.sender); - } + +contract Token is ERC20 { + constructor() ERC20("Test Token", "TestToken") {} + + function mint(uint256 amount) public { + _mint(msg.sender, amount); + } + + function getBalance() public view returns (uint256) { + return balanceOf(msg.sender); + } } diff --git a/package.json b/package.json index 091e091..0c77628 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,20 @@ "private": true, "dependencies": { "@openzeppelin/contracts": "latest", - "@truffle/hdwallet-provider": "^1.6.0", + "@truffle/hdwallet-provider": "^2.1.15", "bn.js": "^5.2.0", "buffer": "^6.0.3", "crypto-js": "^4.1.1", "delphinus-curves": "git+ssh://git@github.com:DelphinusLab/delphinus-curves.git", - "zkwasm-deployment": "file:../zkWasm-deployment", "fs-extra": "^10.0.0", "mongodb": "^4.1.3", "node-fetch": "^2.6.6", "process": "^0.11.10", - "truffle": "latest", - "web3": "^1.7.5", - "web3subscriber": "git+ssh://git@github.com:DelphinusLab/delphinus-web3subscriber.git" + "truffle": "^5.5.19", + "web3": "^4.2.2", + "web3-providers-http": "^4.1.0", + "web3subscriber": "git+ssh://git@github.com:DelphinusLab/delphinus-web3subscriber.git#feature/ethers", + "zkwasm-deployment": "file:../zkWasm-deployment" }, "scripts": { "dev": "npx truffle dev", @@ -33,10 +34,11 @@ "@types/crypto-js": "^4.0.2", "@types/fs-extra": "^9.0.12", "@types/jest": "^26.0.15", + "@types/lru-cache": "^5.1.1", "@types/node": "^18.15.5", "@types/retry": "^0.12.2", - "@types/lru-cache": "^5.1.1", "@types/sha256": "^0.2.0", + "ethers": "^6.9.0", "jest": "26.6.0", "retry": "^0.13.1", "ts-jest": "^26.0.0", diff --git a/src/clients/README.md b/src/clients/README.md new file mode 100644 index 0000000..7fcb981 --- /dev/null +++ b/src/clients/README.md @@ -0,0 +1,129 @@ +## Example usage for L1ServerClient and L1BrowserClient + +### L1 Server Client + +Requires the config file to be present in the `zkwasm-deployment` directory. + +A simple example to mint ERC-20 tokens and transfer them to another account. + +```typescript +import { withL1ServerClient, L1ServerClient } from "src/clients/client"; +import { getConfigByChainName } from "zkwasm-deployment/src/config"; +import { TxBinder } from "web3subscriber/src/txbinder"; +import { L1ClientRole } from "zkwasm-deployment/src/types"; + +async function main(configName: string, targetAccount: string) { + let config = await getConfigByChainName(L1ClientRole.Monitor, configName); + let account = config.monitorAccount; + + // Setup the client by providing the config with required information + await withL1ServerClient(config, async (l1client: L1ServerClient) => { + // Access underlying ethers providers and signers + const { signer, provider } = l1client; + + // Signer is only available if the config has a private key, otherwise the client will be in read-only mode + + // Access the deployed contracts configured with this client + // Similarly, these are read-only if the config does not have a private key + let gasToken = l1client.getGasContract(); + let token = l1client.getTokenContract(); + let proxy = l1client.getProxyContract(); + + // Use TxBinder to assign callbacks to transaction events + // This is optional, but useful for debugging or certain patterns + let txbinder = new TxBinder(); + + // Assign callbacks to transaction events + // Note the "mint" action string is arbitrary, they are used to identify the transaction and execution in txbinder. + txbinder.when("mint", "transactionHash", (tx) => + console.log("transactionHash: ", tx?.hash) + ); + txbinder.when("mint", "transactionReceipt", (receipt) => + console.log("receipt", receipt) + ); + txbinder.when("mint", "error", (err) => console.log("error", err)); + + // Execute the transaction by providing method with an ethers transaction + await txbinder.execute("mint", () => { + return token.mint(BigInt("10000000000000000000")); + }); + + // Directly call the implemented contract methods. + let balance = await token.balanceOf(account); + + await txbinder.execute("transfer", () => { + return token.transfer(targetAccount, BigInt("10000000000000000000")); + }); + + // Access any underlying unimplemented contract methods + // Check ethers v6 documentation for more information + let decimals = token.getEthersContract().decimals.staticCall(); + let name = token.getEthersContract().name.staticCall(); + + // Manual contract call + + let tx = await token + .getEthersContract() + .transfer.send(targetAccount, BigInt("10000000000000000000")); + + // Wait for the transaction to be mined + let receipt = await tx.wait(); + + // Manual ethers transaction + let tx = await signer.sendTransaction({ + to: targetAccount, + value: 0, + data: "0x", + }); + + // Wait for the transaction to be mined + await tx.wait(); + }); +} + +main(process.argv[2], process.argv[3]); +``` + +### L1 Browser Client + +```typescript +import { withL1BrowserClient, L1BrowserClient } from "../../src/clients/client"; + +async function BrowserExample() { + await withL1BrowserClient( + { chainId: 115511, chainName: "sepolia" }, + async (l1client: L1BrowserClient) => { + // Connect to the browser provider such as Metamask + // Most actions will be performed by the browser provider + + // init will ask the user to switch networks to the provided config network + await l1client.init(); + + const { connector } = l1client; + // Connector information such as provider, user address, etc. + + const address = (await connector.getJsonRpcSigner()).address; + console.log("address: ", address); + + // Contract methods can be called by the client + let token = await l1client.getTokenContract(); + let proxy = await l1client.getProxyContract(); + + // The client can call the functions implemented in the contract class + let approval = await token.approve( + await proxy.getEthersContract().getAddress(), + BigInt("10000000000000000000") + ); + + await approval.wait(); + + // Directly call the contract method if necessary + let tx = await token.getEthersContract().transfer.send(BigInt(100)); + + await tx.wait(); + + console.log("transactionHash: ", tx?.hash); + } + ); +} +``` diff --git a/src/clients/client.ts b/src/clients/client.ts index c4ff441..4f4fb2e 100644 --- a/src/clients/client.ts +++ b/src/clients/client.ts @@ -1,28 +1,26 @@ import BN from "bn.js"; -import { - DelphinusWeb3, - Web3BrowsersMode, - Web3ProviderMode, -} from "web3subscriber/src/client"; import { encodeL1address } from "web3subscriber/src/addresses"; import { ChainConfig } from "zkwasm-deployment/src/types"; import { ProxyContract } from "./contracts/proxy"; import { TokenContract } from "./contracts/token"; import { GasContract } from "./contracts/gas"; import { - DelphinusHDWalletProvider, - DelphinusHttpProvider, - DelphinusWsProvider, + DelphinusBrowserConnector, + DelphinusReadOnlyConnector, + DelphinusWalletConnector, + GetBaseProvider, } from "web3subscriber/src/provider"; const L1ADDR_BITS = 160; -function getDelphinusProviderFromConfig(config: ChainConfig) { - // FIXME: use ethers +function getDelphinusConnectorFromConfig(config: ChainConfig) { if (config.privateKey === "") { - return new DelphinusWsProvider(config.wsSource); + return new DelphinusReadOnlyConnector(config.wsSource); } else { - return new DelphinusHDWalletProvider(config.privateKey, config.rpcSource); + return new DelphinusWalletConnector( + config.privateKey, + GetBaseProvider(config.rpcSource) + ); } } @@ -44,32 +42,113 @@ export function dataToBN(data: any) { return new BN(data, 16); } -export class L1Client { - readonly web3: DelphinusWeb3; - private readonly config: ChainConfig; - - constructor(config: ChainConfig, clientMode: boolean) { - if (clientMode) { - this.web3 = new Web3BrowsersMode(); - } else { - this.web3 = new Web3ProviderMode({ - provider: getDelphinusProviderFromConfig(config), - monitorAccount: config.monitorAccount, - }); - } +export type DelphinusConnector = + | DelphinusBrowserConnector + | DelphinusReadOnlyConnector + | DelphinusWalletConnector; + +export type MaybePromise = T | Promise; + +// Abstract class for DelphinusClient which should be implemented by client classes with necessary functions +export abstract class DelphinusClient { + abstract connector: T; + abstract getProxyContract(account?: string): MaybePromise; + abstract getGasContract( + address?: string, + account?: string + ): MaybePromise; + abstract getTokenContract( + address?: string, + account?: string + ): MaybePromise; + + abstract getDefaultAccount(): MaybePromise | undefined; +} + +// L1 Browser Client to be used only in browser environments and typically with an injected/external wallet provider such as metamask. +export class L1BrowserClient extends DelphinusClient { + readonly connector: DelphinusBrowserConnector; + + // some fields for the client which are different to server client + readonly chainName: string; + readonly chainId: string; // Store as string due to indexing json map in deployment repo + + // Pass in some params to the constructor to initialize the client + constructor(chainName: string, chainId: number) { + super(); + this.chainName = chainName; + this.chainId = chainId.toString(); + this.connector = new DelphinusBrowserConnector(); + } + + async init() { + console.log(`init_proxy on %s`, this.chainName); + await this.switchNet(); + } + + getChainIdHex() { + return "0x" + new BN(this.chainId).toString(16); + } + + async getDefaultAccount() { + return (await this.connector.getJsonRpcSigner()).getAddress(); + } + + async getProxyContract(): Promise { + return new ProxyContract( + ProxyContract.getContractAddress(this.chainId), + await this.connector.getJsonRpcSigner() + ); + } + + async getGasContract(address?: string): Promise { + return new GasContract( + address || GasContract.getContractAddress(this.chainId), + await this.connector.getJsonRpcSigner() + ); + } + + async getTokenContract(address?: string): Promise { + return new TokenContract( + address || TokenContract.getContractAddress(this.chainId), + await this.connector.getJsonRpcSigner() + ); + } + + private async switchNet() { + await this.connector.switchNet(this.getChainIdHex()); + } +} +// Client to be used in server environments. It uses a private key to sign transactions. +// If no private key is provided, it will be a read-only client. +export class L1ServerClient extends DelphinusClient< + DelphinusWalletConnector | DelphinusReadOnlyConnector +> { + readonly connector: DelphinusWalletConnector | DelphinusReadOnlyConnector; + protected readonly config: ChainConfig; + constructor(config: ChainConfig) { + super(); + let connector = getDelphinusConnectorFromConfig(config); this.config = config; + this.connector = connector; } async init() { console.log(`init_proxy on %s`, this.config.chainName); - await this.web3.connect(); - await this.switchNet(); + // await this.connector.connect(); + } + + get signer() { + if (this.connector instanceof DelphinusReadOnlyConnector) { + return undefined; + } + return this.connector.signer; } - async close() { - await this.web3.close(); + get provider() { + return this.connector.provider; } getChainIdHex() { @@ -77,40 +156,27 @@ export class L1Client { } getDefaultAccount() { - return this.web3.getDefaultAccount(); + return this.signer ? this.signer.address : undefined; } - getProxyContract(account?: string) { + getProxyContract() { return new ProxyContract( - this.web3, ProxyContract.getContractAddress(this.config.deviceId), - account + this.signer || this.provider ); } - getGasContract(address?: string, account?: string) { + getGasContract(address?: string) { return new GasContract( - this.web3, address || GasContract.getContractAddress(this.config.deviceId), - account + this.signer || this.provider ); } - getTokenContract(address?: string, account?: string) { + getTokenContract(address?: string) { return new TokenContract( - this.web3, address || TokenContract.getContractAddress(this.config.deviceId), - account - ); - } - - private async switchNet() { - await this.web3.switchNet( - this.getChainIdHex(), - this.config.chainName, - this.config.rpcSource, - this.config.nativeCurrency, - this.config.blockExplorer + this.signer || this.provider ); } @@ -130,16 +196,36 @@ export class L1Client { } } -export async function withL1Client( +export async function withL1ServerClient( config: ChainConfig, - clientMode: boolean, - cb: (_: L1Client) => Promise + cb: (_: L1ServerClient) => Promise +) { + const l1Client = new L1ServerClient(config); + await l1Client.init(); + try { + return await cb(l1Client); + } catch (e) { + console.error(e); + throw e; + } +} + +export async function withL1BrowserClient( + { + chainName, + chainId, + }: { + chainName: string; + chainId: number; + }, + cb: (_: L1BrowserClient) => Promise ) { - const l1Client = new L1Client(config, clientMode); + const l1Client = new L1BrowserClient(chainName, chainId); await l1Client.init(); try { return await cb(l1Client); - } finally { - await l1Client.close(); + } catch (e) { + console.error(e); + throw e; } } diff --git a/src/clients/contracts/gas.ts b/src/clients/contracts/gas.ts index 7f67748..967b74c 100644 --- a/src/clients/contracts/gas.ts +++ b/src/clients/contracts/gas.ts @@ -1,10 +1,10 @@ -import { DelphinusContract, DelphinusWeb3 } from "web3subscriber/src/client"; +import { DelphinusContract } from "web3subscriber/src/client"; import { contractsInfo } from "zkwasm-deployment/config/contractsinfo"; -import BN from "bn.js"; +import { Signer, Provider } from "ethers"; export class GasContract extends DelphinusContract { - constructor(web3: DelphinusWeb3, address: string, account?: string) { - super(web3, GasContract.getJsonInterface(), address, account); + constructor(address: string, signerOrProvider: Signer | Provider) { + super(address, GasContract.getJsonInterface().abi, signerOrProvider); } static getJsonInterface(): any { @@ -15,19 +15,19 @@ export class GasContract extends DelphinusContract { return contractsInfo.addressMap.gasToken[chainId].address; } - approve(address: string, amount: BN) { - return this.getWeb3Contract().methods.approve(address, amount).send(); + approve(address: string, amount: BigInt) { + return this.getEthersContract().approve.send(address, amount); } balanceOf(account: string) { - return this.getWeb3Contract().methods.balanceOf(account).call(); + return this.getEthersContract().balanceOf.staticCall(account); } - mint(amount: BN) { - return this.getWeb3Contract().methods.mint(amount).send(); + mint(amount: BigInt) { + return this.getEthersContract().mint.send(amount); } - transfer(address: string, amount: BN) { - return this.getWeb3Contract().methods.transfer(address, amount).send(); + transfer(address: string, amount: BigInt) { + return this.getEthersContract().transfer.send(address, amount); } } diff --git a/src/clients/contracts/proxy.ts b/src/clients/contracts/proxy.ts index 1ffa3d9..489bce5 100644 --- a/src/clients/contracts/proxy.ts +++ b/src/clients/contracts/proxy.ts @@ -1,16 +1,18 @@ import BN from "bn.js"; import * as retry from "retry"; -import { DelphinusContract, DelphinusWeb3 } from "web3subscriber/src/client"; +import { DelphinusContract } from "web3subscriber/src/client"; import { decodeL1address } from "web3subscriber/src/addresses"; -import { PromiseBinder } from "web3subscriber/src/pbinder"; +import { TxBinder } from "web3subscriber/src/txbinder"; import { TokenContract } from "./token"; -import { TxDeposit, TxData } from "../../index"; +import { TxDeposit } from "../../index"; import { extraTokens, Chains, contractsInfo, } from "zkwasm-deployment/config/contractsinfo"; +import { Provider, Signer } from "ethers"; + const registeredTokens = contractsInfo.tokens.concat(extraTokens); /* @@ -53,8 +55,8 @@ export interface WithDraw { } export interface RidInfo { - rid: BN; - batch_size: BN; + rid: bigint; + batch_size: bigint; } function hexcmp(x: string, y: string) { @@ -64,8 +66,8 @@ function hexcmp(x: string, y: string) { } export class ProxyContract extends DelphinusContract { - constructor(web3: DelphinusWeb3, address: string, account?: string) { - super(web3, ProxyContract.getJsonInterface(), address, account); + constructor(address: string, signerOrProvider: Signer | Provider) { + super(address, ProxyContract.getJsonInterface().abi, signerOrProvider); } static getJsonInterface(): any { @@ -77,33 +79,39 @@ export class ProxyContract extends DelphinusContract { } static checkAddHexPrefix(hexStr: string) { - let s:string = hexStr; - if(s.substring(0,2) != "0x") - s = "0x" + s; + let s: string = hexStr; + if (s.substring(0, 2) != "0x") s = "0x" + s; return s; } getProxyInfo() { - return this.getWeb3Contract().methods.getProxyInfo().call(); + return this.getEthersContract().getProxyInfo.staticCall(); } allTokens(): Promise { - return this.getWeb3Contract().methods.allTokens().call(); + return this.getEthersContract().allTokens.staticCall(); } - addToken(tokenid: BN) { - return this.getWeb3Contract().methods.addToken(tokenid).send(); + addToken(tokenid: BigInt) { + return this.getEthersContract().addToken.send(tokenid); } - private _verify(calldata: string, verifydata: BN[], verifyInstance: BN[], aux: BN[], instances: string[][], rid: RidInfo) { - const calldataChecked:string = ProxyContract.checkAddHexPrefix(calldata); + private _verify( + calldata: string, + verifydata: BigInt[], + verifyInstance: BigInt[], + aux: BigInt[], + instances: BigInt[][], + rid: RidInfo + ) { + const calldataChecked: string = ProxyContract.checkAddHexPrefix(calldata); console.log("preparing verify", calldataChecked); console.log("preparing verify", verifydata); console.log("preparing verify", verifyInstance); console.log("preparing verify", instances); - const tx = this.getWeb3Contract().methods.verify( + const tx = this.getEthersContract().verify( calldataChecked, verifydata, verifyInstance, @@ -111,62 +119,67 @@ export class ProxyContract extends DelphinusContract { instances, { rid: rid.rid.toString(), - batch_size: rid.batch_size.toString() - }, + batch_size: rid.batch_size.toString(), + } ); console.log("start send"); - return tx.send(); + return tx; } private _setVerifier(verifierAddress: string) { - return this.getWeb3Contract() - .methods.setVerifier(verifierAddress) - .send(); + return this.getEthersContract().setVerifier.send(verifierAddress); } - verify(calldata: string, verifydata: BN[], verifyInstance: BN[], aux: BN[], instances: string[][], rid: RidInfo) { - const pbinder = new PromiseBinder(); + verify( + calldata: string, + verifydata: BigInt[], + verifyInstance: BigInt[], + aux: BigInt[], + instances: BigInt[][], + rid: RidInfo + ) { + const pbinder = new TxBinder(); - return pbinder.return(async () => { - return await pbinder.bind( - "Verify", - this._verify(calldata, verifydata, verifyInstance, aux, instances, rid) - ); - }); + return pbinder.bind("Verify", () => + this._verify(calldata, verifydata, verifyInstance, aux, instances, rid) + ); } - approve_deposit ( + async approve_deposit( tokenContract: TokenContract, txdeposit: TxDeposit, - l1account: string, + l1account: string ) { - const pbinder = new PromiseBinder(); - //TODO assert txdeposit is TxDeposit + const txbinder = new TxBinder(); - return pbinder.return(async () => { - let allowance = await tokenContract.allowanceOf( - l1account, - this.address() - ); - console.log("Allowance is :", allowance.toString()); - pbinder.snapshot("Approve"); - if (allowance.lt(txdeposit.amount)) { - if (!allowance.isZero()) { - await pbinder.bind( - "Approve", - tokenContract.approve(this.address(), new BN(0)) - ); - } - await pbinder.bind( - "Approve", + // Check allowance + + const allowance = await tokenContract.allowanceOf( + l1account, + await this.getEthersContract().getAddress() + ); + console.log("Allowance is :", allowance.toString()); + // check if allowance is less than deposit amount + if (allowance < txdeposit.amount) { + if (!(allowance === BigInt(0))) { + // Reset allowance to 0 + await txbinder.execute("Approve", async () => tokenContract.approve( - this.address(), - new BN(2).pow(new BN(256)).sub(new BN(1)) + await this.getEthersContract().getAddress(), + BigInt(0) ) ); } - console.log("Deposit Info:", txdeposit); - }); + // Set allowance to max + await txbinder.execute("Approve", async () => + tokenContract.approve( + await this.getEthersContract().getAddress(), + BigInt(2) ** BigInt(256) - BigInt(1) + ) + ); + } + console.log("Approval OK, allowance is :", allowance.toString()); + // TODO: Deposit? not in original implementation } async setVerifier(verifierAddress: string) { @@ -257,5 +270,4 @@ export class ProxyContract extends DelphinusContract { chainInfo: _chainInfo, }; } - } diff --git a/src/clients/contracts/token.ts b/src/clients/contracts/token.ts index 989d9e7..8001bce 100644 --- a/src/clients/contracts/token.ts +++ b/src/clients/contracts/token.ts @@ -1,11 +1,11 @@ -import BN from 'bn.js'; -import { DelphinusContract, DelphinusWeb3 } from "web3subscriber/src/client"; -import { PromiseBinder } from "web3subscriber/src/pbinder"; +import BN from "bn.js"; +import { DelphinusContract } from "web3subscriber/src/client"; +import { Signer, Provider } from "ethers"; import { contractsInfo } from "zkwasm-deployment/config/contractsinfo"; export class TokenContract extends DelphinusContract { - constructor(web3: DelphinusWeb3, address: string, account?: string) { - super(web3, TokenContract.getJsonInterface(), address, account); + constructor(address: string, signerOrProvider: Signer | Provider) { + super(address, TokenContract.getJsonInterface().abi, signerOrProvider); } static getJsonInterface(): any { @@ -16,28 +16,30 @@ export class TokenContract extends DelphinusContract { return contractsInfo.addressMap.testToken[chainId].address; } - approve(address: string, amount: BN) { - return this.getWeb3Contract().methods.approve(address, amount).send(); + approve(address: string, amount: BigInt) { + return this.getEthersContract().approve.send(address, amount); } async balanceOf(account: string): Promise { - let amount = await this.getWeb3Contract().methods.balanceOf(account).call(); + let amount = await this.getEthersContract().balanceOf.staticCall(account); + console.log("balanceOf", amount); return new BN(amount, 10); } - async allowanceOf(account: string, spender: string ): Promise { - let amount = await this.getWeb3Contract().methods.allowance(account, spender).call(); - return new BN(amount, 10); + async allowanceOf(account: string, spender: string): Promise { + let amount = (await this.getEthersContract().allowance( + account, + spender + )) as bigint; + console.log("allowanceOf", amount); + return amount; } - - mint(amount: BN) { - return this.getWeb3Contract().methods.mint(amount).send(); + mint(amount: BigInt) { + return this.getEthersContract().mint(amount); } - transfer(address: string, amount: BN) { - return this.getWeb3Contract() - .methods.transfer(address, amount) - .send(); + transfer(address: string, amount: BigInt) { + return this.getEthersContract().transfer.send(address, amount); } } diff --git a/src/index.ts b/src/index.ts index 8f2b9c1..750aa6b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import { getChargeAddress, dataToBN, - L1Client, - withL1Client + L1ServerClient, + withL1ServerClient, } from "./clients/client.js"; import { @@ -12,7 +12,7 @@ import { SwapAck, WithDraw, RidInfo, - ProxyContract + ProxyContract, } from "./clients/contracts/proxy.js"; import { contractsInfo } from "zkwasm-deployment/config/contractsinfo"; @@ -24,90 +24,146 @@ import hexEnc from "crypto-js/enc-hex"; import { encodeL1address } from "web3subscriber/src/addresses"; export interface Tx { - toBinary: (endian: BN.Endianness) => string; + toBinary: (endian: BN.Endianness) => string; } export class Address { - address: string; - constructor(addr: string) { - if(addr.substring(0, 2) == "0x") { - this.address = addr.substring(0, 2); - } else { - this.address = addr; - } - } - toU256Bytes() { - return new BN(this.address, 16, "be").toBuffer("be",32).toString("hex"); + address: string; + constructor(addr: string) { + if (addr.substring(0, 2) == "0x") { + this.address = addr.substring(0, 2); + } else { + this.address = addr; } + } + toU256Bytes() { + return new BN(this.address, 16, "be").toBuffer("be", 32).toString("hex"); + } } export class TxWithdraw { - nonce: BN; - accountIndex: BN; - tokenIndex: BN; - amount: BN; - opcode: BN; + nonce: bigint; + accountIndex: bigint; + tokenIndex: bigint; + amount: bigint; + opcode: bigint; l1address: Address; - constructor(nonce:BN, accountIndex:BN, tokenIndex:BN, amount:BN, l1address: Address, networkId: number) { + constructor( + nonce: bigint, + accountIndex: bigint, + tokenIndex: bigint, + amount: bigint, + l1address: Address, + networkId: number + ) { this.nonce = nonce; this.accountIndex = accountIndex; this.tokenIndex = tokenIndex; this.amount = amount; - this.l1address = new Address(encodeL1address(l1address.address, networkId.toString(16)).toString(16)); - this.opcode = new BN(1); + this.l1address = new Address( + encodeL1address(l1address.address, networkId.toString(16)).toString(16) + ); + this.opcode = BigInt(1); } - toBinary(endian: BN.Endianness) { - let bytes = [ - this.opcode.toBuffer(endian, 1), - this.nonce.toBuffer(endian, 7), - this.accountIndex.toBuffer(endian, 4), - this.tokenIndex.toBuffer(endian, 4), - this.amount.toBuffer(endian, 32), - ] - .map((x) => { - return x.toString("hex"); - }) - .join("") + this.l1address.toU256Bytes(); + // Helper function to convert BigInt to a Buffer + private toBuffer( + bigIntValue: bigint, + size: number, + endian: "le" | "be" = "be" // Default to big-endian + ): Buffer { + const hex = bigIntValue.toString(16).padStart(size * 2, "0"); + let buffer = Buffer.from(hex, "hex"); + + if (endian === "le") { + // Manually reverse the buffer for little-endian + for (let i = 0; i < buffer.length / 2; i++) { + const temp = buffer[i]; + buffer[i] = buffer[buffer.length - 1 - i]; + buffer[buffer.length - 1 - i] = temp; + } + } + + return buffer; + } + + public toBinary(endian: BN.Endianness): string { + let bytes = + [ + this.toBuffer(this.opcode, 1, endian), + this.toBuffer(this.nonce, 7, endian), + this.toBuffer(this.accountIndex, 4, endian), + this.toBuffer(this.tokenIndex, 4, endian), + this.toBuffer(this.amount, 32, endian), + ] + .map((buffer) => buffer.toString("hex")) + .join("") + this.l1address.toU256Bytes(); + return bytes; } } -export class TxDeposit{ - nonce: BN; - accountIndex: BN; - tokenIndex: BN; - amount: BN; - opcode: BN; +export class TxDeposit { + nonce: bigint; + accountIndex: bigint; + tokenIndex: bigint; + amount: bigint; + opcode: bigint; l1address: Address; - constructor(nonce:BN, accountIndex:BN, tokenIndex:BN, amount:BN, l1address: Address) { + constructor( + nonce: bigint, + accountIndex: bigint, + tokenIndex: bigint, + amount: bigint, + l1address: Address + ) { this.nonce = nonce; this.accountIndex = accountIndex; this.tokenIndex = tokenIndex; this.amount = amount; this.l1address = l1address; - this.opcode = new BN(0); + this.opcode = BigInt(0); + } + + // Helper function to convert BigInt to a Buffer + private toBuffer( + bigIntValue: bigint, + size: number, + endian: "le" | "be" = "be" // Default to big-endian + ): Buffer { + const hex = bigIntValue.toString(16).padStart(size * 2, "0"); + let buffer = Buffer.from(hex, "hex"); + + if (endian === "le") { + // Manually reverse the buffer for little-endian + for (let i = 0; i < buffer.length / 2; i++) { + const temp = buffer[i]; + buffer[i] = buffer[buffer.length - 1 - i]; + buffer[buffer.length - 1 - i] = temp; + } + } + + return buffer; } - toBinary(endian: BN.Endianness) { - let bytes = [ - this.opcode.toBuffer(endian, 1), - this.nonce.toBuffer(endian, 7), - this.accountIndex.toBuffer(endian, 4), - this.tokenIndex.toBuffer(endian, 4), - this.amount.toBuffer(endian, 32), - ] - .map((x) => { - return x.toString("hex"); - }) - .join("") + this.l1address.toU256Bytes(); + public toBinary(endian: BN.Endianness): string { + let bytes = + [ + this.toBuffer(this.opcode, 1, endian), + this.toBuffer(this.nonce, 7, endian), + this.toBuffer(this.accountIndex, 4, endian), + this.toBuffer(this.tokenIndex, 4, endian), + this.toBuffer(this.amount, 32, endian), + ] + .map((buffer) => buffer.toString("hex")) + .join("") + this.l1address.toU256Bytes(); + return bytes; } } - export class TxData { oldroot: BN; newroot: BN; @@ -147,13 +203,19 @@ export class TxData { getZkwasmInstances(): string[] { let data = this.getTxData(); let u64inputs = []; - for(var i=0; i x.toString())], // BN format in dec + [this.getVerifierInputs().map((x) => BigInt(x.toString()))], // BN format in dec //[this.getZkwasmInstances()], rid - ) + ); } } export { getChargeAddress, dataToBN, - L1Client, - withL1Client, + L1ServerClient, + withL1ServerClient, ProxyContract, contractsInfo, GasContract, - TokenContract + TokenContract, }; -export type { - ProxyInfo, - TokenInfo, - Deposit, - SwapAck, - WithDraw, - RidInfo -} +export type { ProxyInfo, TokenInfo, Deposit, SwapAck, WithDraw, RidInfo }; diff --git a/tests/deposit_withdraw_test.ts b/tests/deposit_withdraw_test.ts index 57bc4c1..ebae09a 100644 --- a/tests/deposit_withdraw_test.ts +++ b/tests/deposit_withdraw_test.ts @@ -1,94 +1,88 @@ import BN from "bn.js"; -import { TxData, Tx, TxDeposit, TxWithdraw, Address} from "../src/index"; -import { withL1Client, L1Client } from "../src/clients/client"; +import { TxData, Tx, TxDeposit, TxWithdraw, Address } from "../src/index"; +import { withL1ServerClient, L1ServerClient } from "../src/clients/client"; import { getConfigByChainName } from "zkwasm-deployment/src/config"; import { L1ClientRole } from "zkwasm-deployment/src/types"; import { test_verify } from "./test_utils"; -const initial_root: Uint8Array = new Uint8Array([166, 157, 178, 62, 35, 83, 140, 56, 9, 235, 134, 184, 20, 145, 63, 43, 245, 186, 75, 233, 43, 42, 187, 217, 104, 152, 219, 89, 125, 199, 161, 9]); -const withdraw_root: Uint8Array = new Uint8Array([146, 154, 4, 1, 65, 7, 114, 67, 209, 68, 222, 153, 65, 139, 137, 45, 124, 86, 61, 115, 142, 90, 166, 41, 22, 133, 154, 149, 141, 76, 198, 11]); +const initial_root: Uint8Array = new Uint8Array([ + 166, 157, 178, 62, 35, 83, 140, 56, 9, 235, 134, 184, 20, 145, 63, 43, 245, + 186, 75, 233, 43, 42, 187, 217, 104, 152, 219, 89, 125, 199, 161, 9, +]); +const withdraw_root: Uint8Array = new Uint8Array([ + 146, 154, 4, 1, 65, 7, 114, 67, 209, 68, 222, 153, 65, 139, 137, 45, 124, 86, + 61, 115, 142, 90, 166, 41, 22, 133, 154, 149, 141, 76, 198, 11, +]); -const l1account = "D91A86B4D8551290655caCED21856eF6E532F2D4"; +const l1account = "0xD91A86B4D8551290655caCED21856eF6E532F2D4"; // use the funtion when you do not want to set hard code -async function getCurrentRoot(l1client: L1Client) { - let proxy = l1client.getProxyContract(); - let proxyInfo = await proxy.getProxyInfo(); - let currentRoot: string = proxyInfo.merkle_root.toString(); - return currentRoot; +async function getCurrentRoot(l1client: L1ServerClient) { + let proxy = l1client.getProxyContract(); + let proxyInfo = await proxy.getProxyInfo(); + let currentRoot: string = proxyInfo.merkle_root.toString(); + return currentRoot; } async function main() { + let testChain = process.argv[2]; + let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); + + let txdeposit = new TxDeposit( + BigInt(0), + BigInt(0), + BigInt(0), + BigInt(1) << BigInt(12), // BN(1).shln(12) + new Address("D91A86B4D8551290655caCED21856eF6E532F2D4") + ); + + let txdatadeposit = new TxData( + new BN(initial_root, 16, "le"), + new BN(withdraw_root, 16, "le"), + [txdeposit] + ); + + let networkId = parseInt(config.deviceId, 10); + let txwithdraw = new TxWithdraw( + BigInt(0), + BigInt(0), + BigInt(0), + BigInt(1) << BigInt(12), // BN(1).shln(12) + new Address("D91A86B4D8551290655caCED21856eF6E532F2D4"), + networkId + ); + + let txdatawithdraw = new TxData( + new BN(withdraw_root, 16, "le"), + new BN(initial_root, 16, "le"), + [txwithdraw] + ); + + console.log( + "--------------------------- Testing Action: Deposit ---------------------------" + ); + + async function test_deposit(l1client: L1ServerClient) { + let tokenContract = l1client.getTokenContract(); + let proxy = l1client.getProxyContract(); - let testChain = process.argv[2]; - let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); - - let txdeposit = new TxDeposit( - new BN(0), - new BN(0), - new BN(0), - new BN(1).shln(12), - new Address("D91A86B4D8551290655caCED21856eF6E532F2D4") - ); - - let txdatadeposit = new TxData( - new BN(initial_root, 16, "le"), - new BN(withdraw_root, 16, "le"), - [txdeposit] - ); - - let networkId = parseInt(config.deviceId, 10); - let txwithdraw= new TxWithdraw( - new BN(0), - new BN(0), - new BN(0), - new BN(1).shln(12), - new Address("D91A86B4D8551290655caCED21856eF6E532F2D4"), - networkId - ); - - let txdatawithdraw = new TxData( - new BN(withdraw_root, 16, "le"), - new BN(initial_root, 16, "le"), - [txwithdraw] - ); - - - console.log( - "--------------------------- Testing Action: Deposit ---------------------------" - ); - - async function test_deposit(l1client: L1Client) { - let tokenContract = l1client.getTokenContract(); - let proxy = l1client.getProxyContract(); - await proxy.approve_deposit(tokenContract, txdeposit, l1account); - return test_verify( - l1client, - txdatadeposit, - testChain, - "deposit", - ); - } - - - await withL1Client(config, false, (l1client: L1Client) => { - return test_deposit(l1client); - }); - + await proxy.approve_deposit(tokenContract, txdeposit, l1account); + return test_verify(l1client, txdatadeposit, testChain, "deposit"); + } - console.log( - "--------------------------- Testing Action: Withdraw ---------------------------" - ); + await withL1ServerClient(config, async (l1client: L1ServerClient) => { + return await test_deposit(l1client); + }); + console.log( + "--------------------------- Testing Action: Withdraw ---------------------------" + ); - await withL1Client(config, false, (l1client: L1Client) => { - return test_verify( - l1client, - txdatawithdraw, - testChain, - "withdraw" - ); - }); + await withL1ServerClient(config, async (l1client: L1ServerClient) => { + return test_verify(l1client, txdatawithdraw, testChain, "withdraw"); + }); } -main().then(v => {process.exit();}) +main().then((v) => { + process.exit(); +}); diff --git a/tests/gen_inputs.ts b/tests/gen_inputs.ts index 522fc01..b214400 100644 --- a/tests/gen_inputs.ts +++ b/tests/gen_inputs.ts @@ -1,56 +1,58 @@ import BN from "bn.js"; -import { TxData, Tx, TxDeposit, TxWithdraw, Address} from "../src/index"; +import { TxData, Tx, TxDeposit, TxWithdraw, Address } from "../src/index"; import { getConfigByChainName } from "zkwasm-deployment/src/config"; import { L1ClientRole } from "zkwasm-deployment/src/types"; // This is the root hash in little-endian -const initial_root: Uint8Array = new Uint8Array([166, 157, 178, 62, 35, 83, 140, 56, 9, 235, 134, 184, 20, 145, 63, 43, 245, 186, 75, 233, 43, 42, 187, 217, 104, 152, 219, 89, 125, 199, 161, 9]); -const withdraw_root: Uint8Array = new Uint8Array([146, 154, 4, 1, 65, 7, 114, 67, 209, 68, 222, 153, 65, 139, 137, 45, 124, 86, 61, 115, 142, 90, 166, 41, 22, 133, 154, 149, 141, 76, 198, 11]); +const initial_root: Uint8Array = new Uint8Array([ + 166, 157, 178, 62, 35, 83, 140, 56, 9, 235, 134, 184, 20, 145, 63, 43, 245, + 186, 75, 233, 43, 42, 187, 217, 104, 152, 219, 89, 125, 199, 161, 9, +]); +const withdraw_root: Uint8Array = new Uint8Array([ + 146, 154, 4, 1, 65, 7, 114, 67, 209, 68, 222, 153, 65, 139, 137, 45, 124, 86, + 61, 115, 142, 90, 166, 41, 22, 133, 154, 149, 141, 76, 198, 11, +]); function gen_inputs(root: Uint8Array, tx: Tx, comments: string) { - const root_hex = Buffer.from(root).toString("hex"); - //console.log(root_hex); - let txdata = new TxData( - new BN(root_hex, 16, "le"), - new BN(0), - [tx] - ); - let inputs = txdata.getVerifierInputs(); - let publicInputsBytes = inputs.map((x) => { - return x.toBuffer("le", 32).toString("hex") - }).join(""); - console.log("generate zkwasm inputs:", comments); - console.log(publicInputsBytes); - - let privateInputsBytes = txdata.getTxData(); - console.log(privateInputsBytes); - - + const root_hex = Buffer.from(root).toString("hex"); + //console.log(root_hex); + let txdata = new TxData(new BN(root_hex, 16, "le"), new BN(0), [tx]); + let inputs = txdata.getVerifierInputs(); + let publicInputsBytes = inputs + .map((x) => { + return x.toBuffer("le", 32).toString("hex"); + }) + .join(""); + console.log("generate zkwasm inputs:", comments); + console.log(publicInputsBytes); + + let privateInputsBytes = txdata.getTxData(); + console.log(privateInputsBytes); } async function main() { - let testChain = process.argv[2]; - let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); - - let txdeposit = new TxDeposit( - new BN(0), - new BN(0), - new BN(0), - new BN(1).shln(12), - new Address("D91A86B4D8551290655caCED21856eF6E532F2D4") - ); - let networkId = parseInt(config.deviceId, 10); - let txwithdraw = new TxWithdraw( - new BN(0), - new BN(0), - new BN(0), - new BN(1).shln(12), - new Address("D91A86B4D8551290655caCED21856eF6E532F2D4"), - networkId - ); - - gen_inputs(initial_root, txdeposit, "deposit"); - gen_inputs(withdraw_root, txwithdraw, "withdraw"); + let testChain = process.argv[2]; + let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); + + let txdeposit = new TxDeposit( + BigInt(0), + BigInt(0), + BigInt(0), + BigInt(1) << BigInt(12), // BN(1).shln(12) + new Address("D91A86B4D8551290655caCED21856eF6E532F2D4") + ); + let networkId = parseInt(config.deviceId, 10); + let txwithdraw = new TxWithdraw( + BigInt(0), + BigInt(0), + BigInt(0), + BigInt(1) << BigInt(12), // BN(1).shln(12) + new Address("D91A86B4D8551290655caCED21856eF6E532F2D4"), + networkId + ); + + gen_inputs(initial_root, txdeposit, "deposit"); + gen_inputs(withdraw_root, txwithdraw, "withdraw"); } main(); diff --git a/tests/set_verifier_tests.ts b/tests/set_verifier_tests.ts index 58a4b29..a37fa21 100644 --- a/tests/set_verifier_tests.ts +++ b/tests/set_verifier_tests.ts @@ -1,41 +1,45 @@ -import { withL1Client, L1Client } from "../src/clients/client"; +import { withL1ServerClient, L1ServerClient } from "../src/clients/client"; import { getConfigByChainName } from "zkwasm-deployment/src/config"; import { L1ClientRole } from "zkwasm-deployment/src/types"; // async function setVerifierTests(testChain: string) { - let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); - try { - await withL1Client(config, false, async (l1client: L1Client) => { - let proxy = l1client.getProxyContract(); - let infoBeforeSet = await proxy.getProxyInfo(); - let vidBeforeSet = infoBeforeSet["verifier"]; - console.log("Verifier before setVerifier:", vidBeforeSet); - if(vidBeforeSet == "0") { - console.error(`Error: Please deploy contracts on ${testChain}`); - return; - } + let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); + try { + await withL1ServerClient(config, async (l1client: L1ServerClient) => { + let proxy = l1client.getProxyContract(); + let infoBeforeSet = await proxy.getProxyInfo(); + let vidBeforeSet = infoBeforeSet["verifier"]; + console.log("Verifier before setVerifier:", vidBeforeSet); + if (vidBeforeSet == "0") { + console.error(`Error: Please deploy contracts on ${testChain}`); + return; + } - await proxy.setVerifier("0x0000000000000000000000000000000000000000"); + await proxy.setVerifier("0x0000000000000000000000000000000000000000"); - let infoAfterSet = await proxy.getProxyInfo(); - let vidAfterSet = infoAfterSet["verifier"]; - console.log("Verifier after setVerifier:", vidAfterSet); - if(vidAfterSet == "0") { - console.log("SetVerifier Test PASSED"); - } else { - console.error("Error: SetVerifier Test Failed"); - return; - } - }); - } catch (err) { - console.log("%s", err); - } + let infoAfterSet = await proxy.getProxyInfo(); + let vidAfterSet = infoAfterSet["verifier"]; + console.log("Verifier after setVerifier:", vidAfterSet); + if (vidAfterSet == "0") { + console.log("SetVerifier Test PASSED"); + } else { + console.error("Error: SetVerifier Test Failed"); + return; + } + }); + } catch (err) { + console.log("%s", err); + } } async function main() { - console.log("========================== Testing setVerifier =========================="); - await setVerifierTests(process.argv[2]); + console.log( + "========================== Testing setVerifier ==========================" + ); + await setVerifierTests(process.argv[2]); } -main().then(v => {process.exit();}) +main().then((v) => { + process.exit(); +}); diff --git a/tests/test_utils.ts b/tests/test_utils.ts index 7a24ef3..e502577 100644 --- a/tests/test_utils.ts +++ b/tests/test_utils.ts @@ -1,107 +1,133 @@ import BN from "bn.js"; -import { TxData, TxWithdraw, Address} from "../src/index"; -import { withL1Client, L1Client } from "../src/clients/client"; +import { TxData, TxWithdraw, Address } from "../src/index"; +import { withL1ServerClient, L1ServerClient } from "../src/clients/client"; import { RidInfo } from "../src/clients/contracts/proxy"; import { getConfigByChainName } from "zkwasm-deployment/src/config"; import { L1ClientRole } from "zkwasm-deployment/src/types"; -import { Web3ProviderMode } from "web3subscriber/src/client"; -import { DelphinusHttpProvider } from "web3subscriber/src/provider"; import { encodeL1address, toHexStr } from "web3subscriber/src/addresses"; -import { PromiseBinder } from "web3subscriber/src/pbinder"; +import { TxBinder } from "web3subscriber/src/txbinder"; +import { + DelphinusReadOnlyConnector, + GetBaseProvider, +} from "web3subscriber/src/provider"; +import { EventLog } from "ethers"; /* import { RidInfo } from "../src/clients/contracts/proxy"; */ -async function getEvent(action: string, blockNumber: string, testChain: string){ - let config = await getConfigByChainName(L1ClientRole.Monitor, testChain) - let providerConfig = { - provider: new DelphinusHttpProvider(config.rpcSource), - monitorAccount: config.monitorAccount, - }; - let web3 = new Web3ProviderMode(providerConfig); +async function getEvent( + action: string, + blockNumber: number, + testChain: string +) { + console.log("start to get event ..."); + let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); + let readOnlyProvider = new DelphinusReadOnlyConnector(config.rpcSource); + let ProxyJSON = require("../../build/contracts/Proxy.json"); - let contract = web3.getContract(ProxyJSON, ProxyJSON.networks[config.deviceId].address, config.monitorAccount); - let pastEvents = await contract.getWeb3Contract().getPastEvents("allEvents", { - filter: { blockNumber: blockNumber }, - fromBlock: blockNumber, - }) - for(let r of pastEvents){ - console.log( + let contract = readOnlyProvider.getContractWithoutSigner( + ProxyJSON.networks[config.deviceId].address, + ProxyJSON.abi + ); + + let pastEvents = (await contract.getPastEventsFrom( + blockNumber + )) as EventLog[]; + + for (let r of pastEvents) { + console.log( "--------------------- Get L1 Event: %s ---------------------", - r.event - ); - console.log("blockNumber:", r.blockNumber); - console.log("blockHash:", r.blockHash); - console.log("transactionHash:", r.transactionHash); - if(r.returnValues.l2account == "1"){ - if(action != "withdraw"){ - console.log("SideEffect Check Failed: Action" + action + "should not call SideEffect!"); - }else{ - console.log("SideEffect Check: Passed"); - } - }else{ - if(action == "withdraw"){ - console.log("SideEffect Check Failed: Action" + action + "should call SideEffect!"); - }else{ - console.log("SideEffect Check: Passed"); - } + r.transactionHash + ); + + console.log("blockNumber:", r.blockNumber); + console.log("blockHash:", r.blockHash); + console.log("transactionHash:", r.transactionHash); + + console.log("eventName:", r.eventName); + console.log("args:", r.args); + + // Can index by event arg name + if (r.args.l2account === BigInt(1)) { + // Check side effect if deposit or withdraw action. Both should call SideEffect + if (action === "deposit" || action === "withdraw") { + console.log("SideEffect Check: Passed"); + } else { + console.log( + "SideEffect Check Failed: Action " + + action + + " should call SideEffect!" + ); + } + } else { + if (action == "withdraw" || action == "deposit") { + console.log( + "SideEffect Check Failed: Action " + + action + + " should call SideEffect!" + ); + } else { + console.log("SideEffect Check: Passed"); } + } } } // TODO add proof, batchinstance, aux parameters export async function test_verify( - l1client: L1Client, - txdata: TxData, - testChain: string, - action: string, + l1client: L1ServerClient, + txdata: TxData, + testChain: string, + action: string ) { console.log("testing verify ..."); console.log("start to send to:", l1client.getChainIdHex()); - while (true) { - let txhash = ""; - try { - let proxy = l1client.getProxyContract(); - let proxyInfo = await proxy.getProxyInfo(); - console.log("onchain merkle_root", proxyInfo.merkle_root.toString()); - console.assert(proxyInfo.merkle_root == txdata.oldroot); - let ridInfo: RidInfo = { - rid: new BN(proxyInfo.rid), - batch_size: new BN("1") - }; - let tx = txdata.verify( - l1client, - [new BN("0")], // proof - [new BN("0")], // batchinstance - [new BN("0")], // aux - ridInfo - ); - let r = await tx.when("Verify", "transactionHash", (hash: string) => { - console.log("Get transactionHash", hash); - txhash = hash; - }); - console.log("done", r.blockHash); - console.log("Send Transaction Successfully: Passed"); - let e = await getEvent(action, r.blockNumber, testChain); - console.log(e); - console.log("Get AckEvent successfully: Passed"); - return r; - } catch (e: any) { - if (txhash !== "") { - console.log("exception with transactionHash ready", " will retry ..."); - console.log("exception with transactionHash ready", " will retry ..."); - throw e; + // Run once + let txhash = ""; + try { + let proxy = l1client.getProxyContract(); + let proxyInfo = await proxy.getProxyInfo(); + console.log("proxyInfo", proxyInfo); + console.log("onchain merkle_root", proxyInfo.merkle_root.toString()); + console.assert(proxyInfo.merkle_root == txdata.oldroot); + let ridInfo: RidInfo = { + rid: BigInt(proxyInfo.rid), + batch_size: BigInt(1), + }; + let tx = txdata.verify( + l1client, + [BigInt(0)], // proof + [BigInt(0)], // batchinstance + [BigInt(0)], // aux + ridInfo + ); + tx.when("Verify", "transactionHash", (txResponse) => { + console.log("Get transactionHash", txResponse); + txhash = txResponse?.hash!; + }); + + let receipt = await tx.execute("Verify"); + console.log("receipt", receipt); + if (!receipt) return; + console.log("done", receipt.blockHash); + console.log("Send Transaction Successfully: Passed"); + let e = await getEvent(action, receipt.blockNumber, testChain); + console.log(e); + console.log("Get AckEvent successfully: Passed"); + } catch (e: any) { + if (txhash !== "") { + console.log("exception with transactionHash ready", " will retry ..."); + throw e; + } else { + if (e.message == "ESOCKETTIMEDOUT") { + await new Promise((resolve) => setTimeout(resolve, 5000)); + } else if (e.message == "nonce too low") { + console.log("failed on:", l1client.getChainIdHex(), e.message); // not sure + return; } else { - if (e.message == "ESOCKETTIMEDOUT") { - await new Promise((resolve) => setTimeout(resolve, 5000)); - } else if (e.message == "nonce too low") { - console.log("failed on:", l1client.getChainIdHex(), e.message); // not sure - return; - } else { - console.log("Unhandled exception during verify"); - console.log(e); - throw e; - } + console.log("Unhandled exception during verify"); + console.log(e); + throw e; } } } @@ -179,67 +205,65 @@ async function verify( */ export async function mintToken(testChain: string) { - let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); - let account = config.monitorAccount; - let pbinder = new PromiseBinder(); - let r = pbinder.return(async () => { - await withL1Client(config, false, async (l1client: L1Client) => { - let token = l1client.getTokenContract(); - try { - pbinder.snapshot("Mint"); - console.log("mint token:", token.address()); - let balance = await token.balanceOf(account); + let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); + let account = config.monitorAccount; + let txbinder = new TxBinder(); - if(balance.cmp(new BN("10000000000000000")) == -1) { - console.log("Monitor Account's balance before mint:", balance.toString(10)); - await pbinder.bind("mint", token.mint(new BN("10000000000000000"))); - balance = await token.balanceOf(account); - console.log("Monitor Account's balance:", balance.toString(10)); - }else{ - console.log("Monitor Account's balance:", balance.toString(10)); - console.log("Monitor Account Have Enough Test Token To DO DEPOSIT & WITHDRAW TEST, SKIPED MINT"); - } - } catch (err) { - console.log("%s", err); - } - }); - }); - await r.when( - "mint", - "transactionHash", - (hash: string) => console.log(hash) - ); - } + await withL1ServerClient(config, async (l1client: L1ServerClient) => { + let token = l1client.getTokenContract(); + let balance = await token.balanceOf(account); + if (balance.cmp(new BN("10000000000000000")) == -1) { + console.log( + "Monitor Account's balance before mint:", + balance.toString(10) + ); + await txbinder.execute("mint", () => + token.mint(BigInt("10000000000000000")) + ); + balance = await token.balanceOf(account); + console.log("Monitor Account's balance:", balance.toString(10)); + } else { + console.log("Monitor Account's balance:", balance.toString(10)); + console.log( + "Monitor Account Have Enough Test Token To DO DEPOSIT & WITHDRAW TEST, SKIPED MINT" + ); + } + }); +} -export async function addToken(testChain: string){ - let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); - let tokenIndex = 0; - try { - await withL1Client(config, false, async (l1client: L1Client) => { - let proxy = l1client.getProxyContract(); - let token = l1client.getTokenContract(); - let existing_tokens = await proxy.allTokens(); - let tokenUid = encodeL1address(token.address().replace("0x", ""), parseInt(config.deviceId).toString(16)) - let checkExistToken =0; - for(let i=0; i < existing_tokens.length; i++){ - if(existing_tokens[i].token_uid == tokenUid.toString()){ - console.log("Test Token:" + tokenUid+ "Exist"); - tokenIndex = i - console.log("Token Index is:", tokenIndex); - checkExistToken = 1; - } +export async function addToken(testChain: string) { + let config = await getConfigByChainName(L1ClientRole.Monitor, testChain); + let tokenIndex = 0; + try { + await withL1ServerClient(config, async (l1client: L1ServerClient) => { + let proxy = l1client.getProxyContract(); + let token = l1client.getTokenContract(); + let existing_tokens = await proxy.allTokens(); + let tokenUid = encodeL1address( + (await token.getEthersContract().getAddress()).replace("0x", ""), + parseInt(config.deviceId).toString(16) + ); + let checkExistToken = 0; + for (let i = 0; i < existing_tokens.length; i++) { + if (existing_tokens[i].token_uid == tokenUid.toString()) { + console.log("Test Token:" + tokenUid + "Exist"); + tokenIndex = i; + console.log("Token Index is:", tokenIndex); + checkExistToken = 1; } + } - if(checkExistToken == 0){ - console.log(`Adding test token uid: ${tokenUid.toString(16)}`); - let tx = await proxy.addToken(tokenUid); - console.log("Token Index is:", tokenIndex); - } - }); - } catch (err) { - console.log("%s", err); - } - return tokenIndex; + if (checkExistToken == 0) { + console.log(`Adding test token uid: ${tokenUid.toString(16)}`); + + let tx = await proxy.addToken(BigInt("0x" + tokenUid.toString(16))); + console.log("Token Index is:", tokenIndex); + } + }); + } catch (err) { + console.log("%s", err); + } + return tokenIndex; } /* diff --git a/tests/token/mint-gas.ts b/tests/token/mint-gas.ts index 9d65f3f..3476752 100644 --- a/tests/token/mint-gas.ts +++ b/tests/token/mint-gas.ts @@ -1,38 +1,41 @@ -import { withL1Client, L1Client } from "../../src/clients/client"; +import { withL1ServerClient, L1ServerClient } from "../../src/clients/client"; import { getConfigByChainName } from "zkwasm-deployment/src/config"; -import { PromiseBinder } from "web3subscriber/src/pbinder"; +import { TxBinder } from "web3subscriber/src/txbinder"; import { L1ClientRole } from "zkwasm-deployment/src/types"; -import BN from "bn.js"; + async function main(configName: string, targetAccount: string) { let config = await getConfigByChainName(L1ClientRole.Monitor, configName); let account = config.monitorAccount; - let pbinder = new PromiseBinder(); - let r = pbinder.return(async () => { - await withL1Client(config, false, async (l1client: L1Client) => { - let token = l1client.getGasContract(); - // await web3.eth.net.getId(); - try { - console.log("mint token:", token.address()); - let balance = await token.balanceOf(account); - console.log("sender: balance before mint:", balance); - await pbinder.bind("mint", token.mint(new BN("10000000000000000000"))); - balance = await token.balanceOf(account); - console.log("sender: balance after mint", balance); - if (targetAccount) { - await pbinder.bind( - "transfer", - token.transfer(targetAccount, new BN("10000000000000000000")) - ); - balance = await token.balanceOf(targetAccount); - console.log("balance of recipient after transfer", balance); - } - } catch (err) { - console.log("%s", err); - } + + await withL1ServerClient(config, async (l1client: L1ServerClient) => { + console.log("account: ", account); + let token = l1client.getGasContract(); + let txbinder = new TxBinder(); + + txbinder.when("mint", "transactionHash", (tx) => + console.log("transactionHash: ", tx?.hash) + ); + txbinder.when("mint", "transactionReceipt", (receipt) => + console.log("receipt", receipt) + ); + + await txbinder.execute("mint", () => { + return token.mint(BigInt("10000000000000000000")); }); + + let balance = await token.balanceOf(account); + + console.log("sender: balance after mint", balance); + + if (targetAccount) { + await txbinder.execute("transfer", () => { + return token.transfer(targetAccount, BigInt("10000000000000000000")); + }); + + balance = await token.balanceOf(targetAccount); + console.log("balance of recipient after transfer", balance); + } }); - await r.when("mint", "transactionHash", (hash: string) => console.log(hash)); } -/* .once("transactionHash",hash => console.log(hash) */ main(process.argv[2], process.argv[3]); diff --git a/tests/token/mint-token.ts b/tests/token/mint-token.ts index c1e6fea..219c60f 100644 --- a/tests/token/mint-token.ts +++ b/tests/token/mint-token.ts @@ -1,43 +1,39 @@ -import BN from 'bn.js'; -import { withL1Client, L1Client } from "../../src/clients/client"; +import { withL1ServerClient, L1ServerClient } from "../../src/clients/client"; import { getConfigByChainName } from "zkwasm-deployment/src/config"; -import { PromiseBinder } from "web3subscriber/src/pbinder"; +import { TxBinder } from "web3subscriber/src/txbinder"; import { L1ClientRole } from "zkwasm-deployment/src/types"; async function main(configName: string, targetAccount: string) { let config = await getConfigByChainName(L1ClientRole.Monitor, configName); let account = config.monitorAccount; - let pbinder = new PromiseBinder(); - let r = pbinder.return(async () => { - await withL1Client(config, false, async (l1client: L1Client) => { - let token = l1client.getTokenContract(); - // await web3.eth.net.getId(); - try { - pbinder.snapshot("Mint"); - console.log("mint token:", token.address()); - let balance = await token.balanceOf(account); - console.log("sender: balance before mint:", balance); - await pbinder.bind("mint", token.mint(new BN("10000000000000000000"))); - balance = await token.balanceOf(account); - console.log("sender: balance after mint", balance); - if (targetAccount) { - await pbinder.bind( - "transfer", - token.transfer(targetAccount, new BN("10000000000000000000")) - ); - balance = await token.balanceOf(targetAccount); - console.log("balance of recipient after transfer", balance); - } - } catch (err) { - console.log("%s", err); - } + let txbinder = new TxBinder(); + await withL1ServerClient(config, async (l1client: L1ServerClient) => { + let token = l1client.getTokenContract(); + txbinder.when("mint", "transactionHash", (tx) => console.log(tx?.hash)); + txbinder.when("mint", "transactionReceipt", (receipt) => + console.log(receipt) + ); + + await txbinder.execute("mint", () => { + return token.mint(BigInt("10000000000000000000")); }); + + let balance = await token.balanceOf(account); + + console.log("sender: balance after mint", balance); + + let allowance = await token.allowanceOf(account, targetAccount); + console.log("allowance", allowance); + + if (targetAccount) { + await txbinder.execute("transfer", () => { + return token.transfer(targetAccount, BigInt("10000000000000000000")); + }); + + balance = await token.balanceOf(targetAccount); + console.log("balance of recipient after transfer", balance); + } }); - await r.when( - "mint", - "transactionHash", - (hash: string) => console.log(hash) - ); } /* .once("transactionHash",hash => console.log(hash) */ diff --git a/truffle-config.js b/truffle-config.js index cb08633..29519a8 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -18,21 +18,21 @@ * */ -const Web3HttpProvider = require('web3-providers-http'); -const HDWalletProvider = require('@truffle/hdwallet-provider'); -const secrets = require('zkwasm-deployment/config/monitor-secrets.json'); +const Web3HttpProvider = require("web3-providers-http"); +const HDWalletProvider = require("@truffle/hdwallet-provider"); +const secrets = require("zkwasm-deployment/config/monitor-secrets.json"); const http_options = { - keepAlive: true, - timeout: 20000, // milliseconds, - headers: [{name: 'Access-Control-Allow-Origin', value: '*'}], - withCredentials: false + keepAlive: true, + timeout: 20000, // milliseconds, + headers: [{ name: "Access-Control-Allow-Origin", value: "*" }], + withCredentials: false, }; const http_provider = (url) => { - let p = new Web3HttpProvider(url, http_options); - return p; -} + let p = new Web3HttpProvider.HttpProvider(url, http_options); + return p; +}; module.exports = { /** @@ -40,34 +40,42 @@ module.exports = { */ networks: { - goerli: { //eth testnet - provider: () => new HDWalletProvider( - secrets.accounts.deployer.priv, - http_provider("https://goerli.infura.io/v3/" + secrets.infura_id_goerli) //we find ankr does not stable for deployment so have to use infura - //http_provider("https://eth.getblock.io/goerli/?api_key=" + secrets.getblock_key_goerli) - ), - network_id: 5, // goerli's id - gas: 5500000, // goerli has a lower block limit than mainnet - confirmations: 2, // # of confs to wait between deployments. (default: 0) - timeoutBlocks: 400, // # of blocks before a deployment times out (minimum/default: 50) - skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) + goerli: { + //eth testnet + provider: () => + new HDWalletProvider({ + privateKeys: [secrets.accounts.deployer.priv], + providerOrUrl: + "https://goerli.infura.io/v3/" + secrets.infura_id_goerli, + }), //we find ankr does not stable for deployment so have to use infura + //http_provider("https://eth.getblock.io/goerli/?api_key=" + secrets.getblock_key_goerli) + + network_id: 5, // goerli's id + gas: 5500000, // goerli has a lower block limit than mainnet + confirmations: 2, // # of confs to wait between deployments. (default: 0) + timeoutBlocks: 400, // # of blocks before a deployment times out (minimum/default: 50) + skipDryRun: true, // Skip dry run before migrations? (default: false for public nets ) }, - bsctestnet: { //bsc - provider: () => new HDWalletProvider(secrets.accounts.deployer.priv, - http_provider(`https://data-seed-prebsc-1-s3.binance.org:8545`) - //http_provider("https://bsc.getblock.io/testnet/?api_key="+secrets.getblock_key) - ), + bsctestnet: { + //bsc + provider: () => + new HDWalletProvider({ + privateKeys: [secrets.accounts.deployer.priv], + providerOrUrl: "https://data-seed-prebsc-1-s1.binance.org:8545", + //http_provider("https://bsc.getblock.io/testnet/?api_key="+secrets.getblock_key) + }), network_id: 97, confirmations: 10, timeoutBlocks: 200, - skipDryRun: true + skipDryRun: true, }, sepolia: { provider: () => - new HDWalletProvider( - secrets.accounts.deployer.priv, - http_provider("https://sepolia.infura.io/v3/" + secrets.infura_id_sepolia) - ), + new HDWalletProvider({ + privateKeys: [secrets.accounts.deployer.priv], + providerOrUrl: + "https://sepolia.infura.io/v3/" + secrets.infura_id_sepolia, + }), network_id: "11155111", gasLimit: 30000000, timeoutBlocks: 4000, @@ -75,15 +83,17 @@ module.exports = { }, alfajores: { provider: () => - new HDWalletProvider( - secrets, - "https://alfajores-forno.celo-testnet.org" - ), + new HDWalletProvider({ + privateKeys: [secrets.accounts.deployer.priv], + providerOrUrl: + "https://alfajores-forno.celo-testnet.org" /*"https://alfajores.celo-testnet.org"*/, + }), + network_id: "44787", gasLimit: 30000000, timeoutBlocks: 4000, skipDryRun: true, - } + }, }, // Set default mocha options here, use special reporters etc. @@ -94,7 +104,7 @@ module.exports = { // Configure your compilers compilers: { solc: { - version: "native", // Fetch exact version from solc-bin (default: truffle's version) + version: "^0.8.20", // Fetch exact version from solc-bin (default: truffle's version) settings: { optimizer: { enabled: true, @@ -109,6 +119,6 @@ module.exports = { // $ truffle migrate --reset --compile-all db: { - enabled: false - } + enabled: false, + }, }; diff --git a/tsconfig.json b/tsconfig.json index 8f97c8f..733fd72 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es6", + "target": "es2016", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, @@ -17,5 +17,5 @@ "sourceMap": true, "outDir": "./dist" }, - "include": ["src/**/*","tests"] + "include": ["src/**/*", "tests"] }