Skip to content

Commit

Permalink
Merge branch 'lyova-zks-639-add-swaps-to-zksync-js' into nft
Browse files Browse the repository at this point in the history
  • Loading branch information
ly0va committed May 11, 2021
2 parents 7f501ac + c314e13 commit 268b7ca
Show file tree
Hide file tree
Showing 22 changed files with 569 additions and 49 deletions.
6 changes: 5 additions & 1 deletion core/bin/zksync_api/src/fee_ticker/constants.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use zksync_types::{
gas_counter::{CommitCost, VerifyCost},
ChangePubKeyOp, MintNFTOp, TransferOp, TransferToNewOp, WithdrawNFTOp, WithdrawOp,
ChangePubKeyOp, MintNFTOp, SwapOp, TransferOp, TransferToNewOp, WithdrawNFTOp, WithdrawOp,
};

/// Gas cost per chunk to cover constant cost of commit, execute and prove transactions
Expand Down Expand Up @@ -36,13 +36,17 @@ pub(crate) const BASE_CHANGE_PUBKEY_ONCHAIN_COST: u64 = CommitCost::CHANGE_PUBKE
pub(crate) const BASE_MINT_NFT_COST: u64 = VerifyCost::MINT_NFT_COST
+ CommitCost::MINT_TOKEN_COST
+ AMORTIZED_COST_PER_CHUNK * (MintNFTOp::CHUNKS as u64); // TODO Verify this constant
pub(crate) const BASE_SWAP_COST: u64 = CommitCost::SWAP_COST
+ VerifyCost::SWAP_COST
+ AMORTIZED_COST_PER_CHUNK * (SwapOp::CHUNKS as u64);

// The Subsidized cost of operations.
// Represent the cost of performing operations after recursion is introduced to mainnet.
pub(crate) const SUBSIDY_TRANSFER_COST: u64 = BASE_TRANSFER_COST * 5 / 100;
pub(crate) const SUBSIDY_TRANSFER_TO_NEW_COST: u64 = BASE_TRANSFER_TO_NEW_COST * 5 / 100;
pub(crate) const SUBSIDY_WITHDRAW_COST: u64 = BASE_WITHDRAW_COST;
pub(crate) const SUBSIDY_WITHDRAW_NFT_COST: u64 = BASE_WITHDRAW_NFT_COST;
pub(crate) const SUBSIDY_SWAP_COST: u64 = BASE_SWAP_COST;
pub(crate) const SUBSIDY_CHANGE_PUBKEY_OFFCHAIN_COST: u64 = BASE_CHANGE_PUBKEY_OFFCHAIN_COST;
pub(crate) const SUBSIDY_CHANGE_PUBKEY_ONCHAIN_COST: u64 = BASE_CHANGE_PUBKEY_ONCHAIN_COST;
pub(crate) const SUBSIDY_OLD_CHANGE_PUBKEY_OFFCHAIN_COST: u64 =
Expand Down
2 changes: 2 additions & 0 deletions core/bin/zksync_api/src/fee_ticker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl GasOperationsCost {
OutputFeeType::FastWithdraw,
standard_fast_withdrawal_cost.into(),
),
(OutputFeeType::Swap, constants::BASE_SWAP_COST.into()),
(
OutputFeeType::WithdrawNFT,
constants::BASE_WITHDRAW_NFT_COST.into(),
Expand Down Expand Up @@ -159,6 +160,7 @@ impl GasOperationsCost {
OutputFeeType::FastWithdraw,
subsidy_fast_withdrawal_cost.into(),
),
(OutputFeeType::Swap, constants::SUBSIDY_SWAP_COST.into()),
(
OutputFeeType::WithdrawNFT,
constants::SUBSIDY_WITHDRAW_NFT_COST.into(),
Expand Down
4 changes: 2 additions & 2 deletions core/lib/storage/src/chain/block/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ impl NewExecutedTransaction {
serde_json::from_value(tx["recipientAddress"].clone()).unwrap(),
),
ZkSyncTx::Swap(_) => (
serde_json::from_value(tx["submitter_address"].clone()).unwrap(),
serde_json::from_value(tx["submitter_address"].clone()).unwrap(),
serde_json::from_value(tx["submitterAddress"].clone()).unwrap(),
serde_json::from_value(tx["submitterAddress"].clone()).unwrap(),
),
};

Expand Down
1 change: 1 addition & 0 deletions core/lib/types/src/tx/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct Order {
pub price: (BigUint, BigUint),
#[serde(with = "BigUintSerdeAsRadix10Str")]
pub amount: BigUint,
#[serde(flatten)]
pub time_range: TimeRange,
pub signature: TxSignature,
}
Expand Down
16 changes: 13 additions & 3 deletions core/tests/ts-tests/tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import './forced-exit';
import './misc';
import './batch-builder';
import './create2';
import './swap';

const TX_AMOUNT = utils.parseEther('10.0');
// should be enough for ~200 test transactions (excluding fees), increase if needed
Expand Down Expand Up @@ -70,8 +71,8 @@ describe(`ZkSync integration tests (token: ${token}, transport: ${transport})`,
await tester.syncWallet.isERC20DepositsApproved(token, DEPOSIT_AMOUNT),
'Token should not be approved'
).to.be.false;
const approveERC20_next = await tester.syncWallet.approveERC20TokenDeposits(token);
await approveERC20_next.wait();
const approveERC20Next = await tester.syncWallet.approveERC20TokenDeposits(token);
await approveERC20Next.wait();
expect(await tester.syncWallet.isERC20DepositsApproved(token), 'The second deposit should be approved')
.to.be.true;
}
Expand Down Expand Up @@ -138,6 +139,15 @@ describe(`ZkSync integration tests (token: ${token}, transport: ${transport})`,
await tester.testBatchBuilderGenericUsage(david, frank, judy, token, TX_AMOUNT);
});

step('should test swaps and limit orders', async () => {
if (onlyBasic) {
return;
}
const secondToken = token == 'ETH' ? 'wBTC' : 'ETH';
await tester.testSwap(alice, frank, token, secondToken, TX_AMOUNT);
await tester.testSwapBatch(alice, frank, david, token, secondToken, TX_AMOUNT);
});

step('should test multi-signers', async () => {
// At this point, all these wallets already have their public keys set.
await tester.testMultipleBatchSigners([alice, david, frank], token, TX_AMOUNT);
Expand All @@ -156,7 +166,7 @@ describe(`ZkSync integration tests (token: ${token}, transport: ${transport})`,
await tester.testTransferNFT(chuck, alice, token);
});

step('should execute a ForcedExit', async () => {
step('should execute a forced exit', async () => {
if (onlyBasic) {
return;
}
Expand Down
142 changes: 142 additions & 0 deletions core/tests/ts-tests/tests/swap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { Tester } from './tester';
import { expect } from 'chai';
import { Wallet, types, utils, wallet } from 'zksync';
import { BigNumber } from 'ethers';

type TokenLike = types.TokenLike;

declare module './tester' {
interface Tester {
testSwap(
walletA: Wallet,
walletB: Wallet,
tokenA: TokenLike,
tokenB: TokenLike,
amount: BigNumber
): Promise<void>;
testSwapBatch(
walletA: Wallet,
walletB: Wallet,
walletC: Wallet,
tokenA: TokenLike,
tokenB: TokenLike,
amount: BigNumber
): Promise<void>;
}
}

Tester.prototype.testSwap = async function (
walletA: Wallet,
walletB: Wallet,
tokenA: TokenLike,
tokenB: TokenLike,
amount: BigNumber
) {
const { totalFee: fee } = await this.syncProvider.getTransactionFee('Swap', walletA.address(), tokenA);
const stateABefore = (await this.syncProvider.getState(walletA.address())).committed;
const stateBBefore = (await this.syncProvider.getState(walletB.address())).committed;

const orderA = await walletA.getOrder({
tokenSell: tokenA,
tokenBuy: tokenB,
amount,
price: utils.price({
sellPrice: 1,
buyPrice: 2
})
});

const orderB = await walletB.getOrder({
tokenSell: tokenB,
tokenBuy: tokenA,
amount: amount.mul(2),
price: utils.price({
sellPrice: 2,
buyPrice: 1
})
});

const swap = await walletA.syncSwap({
orders: [orderA, orderB],
feeToken: tokenA,
fee
});

const receipt = await swap.awaitReceipt();
expect(receipt.success, `Swap transaction failed with a reason: ${receipt.failReason}`).to.be.true;

const stateAAfter = (await this.syncProvider.getState(walletA.address())).committed;
const stateBAfter = (await this.syncProvider.getState(walletB.address())).committed;

const diffA = {
tokenA: BigNumber.from(stateABefore.balances[tokenA] || 0).sub(stateAAfter.balances[tokenA] || 0),
tokenB: BigNumber.from(stateAAfter.balances[tokenB] || 0).sub(stateABefore.balances[tokenB] || 0),
nonce: stateAAfter.nonce - stateABefore.nonce
};
const diffB = {
tokenB: BigNumber.from(stateBBefore.balances[tokenB] || 0).sub(stateBAfter.balances[tokenB] || 0),
tokenA: BigNumber.from(stateBAfter.balances[tokenA] || 0).sub(stateBBefore.balances[tokenA] || 0),
nonce: stateBAfter.nonce - stateBBefore.nonce
};

expect(diffA.tokenA.eq(amount.add(fee)), 'Wrong amount after swap (walletA, tokenA)').to.be.true;
expect(diffA.tokenB.eq(amount.mul(2)), 'Wrong amount after swap (walletA, tokenB)').to.be.true;
expect(diffB.tokenB.eq(amount.mul(2)), 'Wrong amount after swap (walletB, tokenB)').to.be.true;
expect(diffB.tokenA.eq(amount), 'Wrong amount after swap (walletB, tokenA)').to.be.true;
expect(diffA.nonce, 'Wrong nonce after swap (wallet A)').to.eq(1);
expect(diffB.nonce, 'Wrong nonce after swap (wallet B)').to.eq(1);

this.runningFee = this.runningFee.add(fee);
};

Tester.prototype.testSwapBatch = async function (
walletA: Wallet,
walletB: Wallet,
walletC: Wallet,
tokenA: TokenLike,
tokenB: TokenLike,
amount: BigNumber
) {
const nonceBefore = await walletA.getNonce();

// these are limit orders, so they can be reused
const orderA = await walletA.getLimitOrder({
tokenSell: tokenA,
tokenBuy: tokenB,
price: utils.price({
sellPrice: 2,
buyPrice: 5
})
});

const orderB = await walletB.getLimitOrder({
tokenSell: tokenB,
tokenBuy: tokenA,
price: utils.price({
sellPrice: 4,
buyPrice: 1
})
});

const batch = await walletC
.batchBuilder()
.addSwap({
orders: [orderA, orderB],
amounts: [amount.div(5), amount.div(2)],
feeToken: tokenA
})
.addSwap({
orders: [orderB, orderA],
amounts: [amount, amount.div(4)],
feeToken: tokenA
})
.build(tokenA);

const handles = await wallet.submitSignedTransactionsBatch(this.syncProvider, batch.txs, [batch.signature]);
await Promise.all(handles.map((handle) => handle.awaitReceipt()));

const nonceAfter = await walletA.getNonce();
expect(nonceAfter, 'Nonce should not increase after limit order is partially filled').to.eq(nonceBefore);

this.runningFee = this.runningFee.add(batch.totalFee.get(tokenA) || 0);
};
1 change: 1 addition & 0 deletions infrastructure/zk/src/run/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export async function tokenInfo(address: string) {
// installs all dependencies and builds our js packages
export async function yarn() {
await utils.spawn('yarn');
await utils.spawn('yarn crypto build');
await utils.spawn('yarn reading-tool build');
await utils.spawn('yarn zksync prepublish');
}
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"workspaces": {
"packages": [
"sdk/zksync.js",
"sdk/zksync-crypto",
"contracts",
"infrastructure/zcli",
"infrastructure/explorer",
Expand All @@ -25,6 +26,7 @@
},
"scripts": {
"zksync": "yarn workspace zksync",
"crypto": "yarn workspace zksync-crypto",
"contracts": "yarn workspace franklin-contracts",
"zcli": "yarn workspace zcli",
"ts-tests": "yarn workspace ts-tests",
Expand Down
17 changes: 10 additions & 7 deletions sdk/zksync-crypto/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ which wasm-pack || cargo install wasm-pack

# pack for bundler (!note this verion is used in the pkg.browser field)
wasm-pack build --release --target=bundler --out-name=zksync-crypto-bundler --out-dir=dist
# pack for browser
wasm-pack build --release --target=web --out-name=zksync-crypto-web --out-dir=dist
# pack for node.js
wasm-pack build --release --target=nodejs --out-name=zksync-crypto-node --out-dir=dist

rm dist/package.json dist/.gitignore

if [ "$CI" == "1" ]; then
exit 0
fi

# convert the bundler build into JS in case the environment doesn't support WebAssembly
../build_binaryen.sh
Expand All @@ -21,11 +31,4 @@ sed -i.backup "s/^import.*/\
let wasm = require('.\/zksync-crypto-bundler_bg_asm.js');/" ./dist/zksync-crypto-bundler_asm.js
sed -i.backup "s/\.js/_asm\.js/g" $ASM

# pack for browser
wasm-pack build --release --target=web --out-name=zksync-crypto-web --out-dir=dist

# pack for node.js
wasm-pack build --release --target=nodejs --out-name=zksync-crypto-node --out-dir=dist

rm dist/package.json dist/.gitignore
rm dist/*.backup
2 changes: 1 addition & 1 deletion sdk/zksync-crypto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
],
"scripts": {
"build": "./build.sh && node web-wasm.js",
"test": "f cargo test"
"test": "zk f cargo test"
}
}
7 changes: 7 additions & 0 deletions sdk/zksync-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ pub fn rescue_hash_tx_msg(msg: &[u8]) -> Vec<u8> {
utils::rescue_hash_tx_msg(msg)
}

/// `msg` should be represented by 2 concatenated
/// serialized orders of the swap transaction
#[wasm_bindgen(js_name = "rescueHashOrders")]
pub fn rescue_hash_orders(msg: &[u8]) -> Vec<u8> {
utils::rescue_hash_orders(msg)
}

#[wasm_bindgen]
/// We use musig Schnorr signature scheme.
/// It is impossible to restore signer for signature, that is why we provide public key of the signer
Expand Down
Loading

0 comments on commit 268b7ca

Please sign in to comment.