Skip to content

Commit

Permalink
chore: Add trade task
Browse files Browse the repository at this point in the history
  • Loading branch information
akashiceth committed Jun 10, 2022
1 parent b8888c2 commit d3c8c8e
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 23 deletions.
2 changes: 1 addition & 1 deletion contracts/BendExchange.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ contract BendExchange is IBendExchange, ReentrancyGuard, Ownable {
using OrderTypes for OrderTypes.TakerOrder;

string public constant NAME = "BendExchange";
string public constant VERSION = "1.0";
string public constant VERSION = "1";

address public immutable WETH;
bytes32 public immutable DOMAIN_SEPARATOR;
Expand Down
5 changes: 5 additions & 0 deletions tasks/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,8 @@ export const ProtocolFee: Params<number> = {
[Network.rinkeby]: 200,
[Network.main]: 200,
};

export const BendAddressesProviders: Params<string> = {
[Network.rinkeby]: "0xE55870eBB007a50B0dfAbAdB1a21e4bFcee5299b",
[Network.main]: "",
};
2 changes: 2 additions & 0 deletions tasks/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ task("deploy:full", "Deploy all contracts").setAction(async (_, { network, run }

const currencyManager = await deployContract("CurrencyManager", [], true);

await currencyManager.connect(deployer).addCurrency(weth);

const executionManager = await deployContract("ExecutionManager", [], true);
const strategyStandardSaleForFixedPrice = await deployContract(
"StrategyStandardSaleForFixedPrice",
Expand Down
103 changes: 103 additions & 0 deletions tasks/trade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { BigNumber, constants } from "ethers";
import { defaultAbiCoder, parseEther } from "ethers/lib/utils";
import { task } from "hardhat/config";
import { createSignedMakerOrder, createTakerOrder } from "../test/helpers/order-helper";
import { MakerOrderWithSignature } from "../test/helpers/order-types";
import { getParamPerNetwork, WETH, BendAddressesProviders } from "./config";
import { getContractFromDB, getContractAddressFromDB, getChainId, getContract, waitForTx } from "./utils/helpers";

task("trade:BNFT", "test trade BNFT")
.addParam("maker", "address of maker")
.addParam("makerkey", "private key of maker")
.addParam("taker", "address of taker")
.addParam("takerkey", "private key of taker")
.addParam("nft", "address of nft")
.addParam("tokenid", "token id of nft")
.setAction(async ({ maker, makerkey, taker, takerkey, nft, tokenid }, { ethers, network, run }) => {
await run("set-DRE");
const chainId = await getChainId();
console.log(`chainId: ${chainId}`);
console.log(`maker: ${maker}`);
console.log(`taker: ${taker}`);
console.log(`nft: ${nft}`);
console.log(`tokenid: ${tokenid}`);

const makerSigner = new ethers.Wallet(makerkey, ethers.provider);
const takerSigner = new ethers.Wallet(takerkey, ethers.provider);
const emptyEncodedBytes = defaultAbiCoder.encode([], []);
const bendAddressesProviders = getParamPerNetwork(BendAddressesProviders, network.name);
const price = parseEther("1");
const weth = getParamPerNetwork(WETH, network.name);

// check proxy
const authManager = await getContractFromDB("AuthorizationManager");
if ((await authManager.proxies(maker)) === constants.AddressZero) {
console.log("register maker proxy");
waitForTx(await authManager.connect(makerSigner).registerProxy());
}
if ((await authManager.proxies(taker)) === constants.AddressZero) {
console.log("register taker proxy");
waitForTx(await authManager.connect(takerSigner).registerProxy());
}

const makerProxy = await authManager.proxies(maker);
const takerProxy = await authManager.proxies(taker);
// check allowance
const wethContract = await getContract("WETH", weth);
const allowance = await wethContract.allowance(taker, takerProxy);
if (allowance.lt(price)) {
console.log("approve taker weth");
waitForTx(await wethContract.connect(takerSigner).approve(takerProxy, constants.MaxUint256));
}
const nftContract = await getContract("ERC721", nft);
const nftAllowance = await nftContract.isApprovedForAll(maker, makerProxy);
if (!nftAllowance) {
console.log("approve maker nft");
waitForTx(await nftContract.connect(makerSigner).setApprovalForAll(makerProxy, true));
}

const startTimeNow = BigNumber.from(
(await ethers.provider.getBlock(await ethers.provider.getBlockNumber())).timestamp
);
const startTimeOrder = startTimeNow.sub(3600 * 24);
const endTimeOrder = startTimeNow.add(3600 * 24);

const bendExchange = await getContractFromDB("BendExchange");
const interceptorAddress = await getContractAddressFromDB("RedeemNFT");
const interceptorExtra = defaultAbiCoder.encode(["address"], [bendAddressesProviders]);

const makerAskOrder: MakerOrderWithSignature = await createSignedMakerOrder({
isOrderAsk: true,
maker: maker,
interceptor: interceptorAddress,
interceptorExtra: interceptorExtra,
collection: nft,
price,
tokenId: tokenid,
amount: constants.One,
strategy: await getContractAddressFromDB("StrategyStandardSaleForFixedPrice"),
currency: weth,
nonce: constants.Zero,
startTime: startTimeOrder,
endTime: endTimeOrder,
minPercentageToAsk: BigNumber.from(9800),
params: emptyEncodedBytes,
privateKey: makerkey,
chainId,
verifyingContract: bendExchange.address,
});
const takerBidOrder = createTakerOrder({
isOrderAsk: false,
taker: taker,
price,
tokenId: tokenid,
minPercentageToAsk: BigNumber.from(9800),
params: emptyEncodedBytes,
interceptor: constants.AddressZero,
interceptorExtra: emptyEncodedBytes,
});

await bendExchange.connect(takerSigner).matchAskWithTakerBidUsingETHAndWETH(takerBidOrder, makerAskOrder, {
value: price,
});
});
10 changes: 9 additions & 1 deletion tasks/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const registerContractInJsonDB = async (contractId: string, contractInsta
}).write();
};

export const getContractAddressInDB = async (id: string): Promise<string> => {
export const getContractAddressFromDB = async (id: string): Promise<string> => {
const contractAtDb = DB.get(id).value();
if (contractAtDb?.address) {
return contractAtDb.address;
Expand Down Expand Up @@ -67,6 +67,14 @@ export const withSaveAndVerify = async (
return instance;
};

export const getChainId = async (): Promise<number> => {
return (await DRE.ethers.provider.getNetwork()).chainId;
};

export const getContract = async (contractName: string, address: string): Promise<Contract> => {
return await DRE.ethers.getContractAt(contractName, address);
};

export const getContractFromDB = async (id: string): Promise<Contract> => {
return getContract(id, await getContractAddressFromDB(id));
};
2 changes: 1 addition & 1 deletion test/_setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ export function makeSuite(name: string, tests: (contracts: Contracts, env: Env,
// Verify the domain separator is properly computed
assert.equal(
await contracts.bendExchange.DOMAIN_SEPARATOR(),
computeDomainSeparator(contracts.bendExchange.address)
computeDomainSeparator((await ethers.provider.getNetwork()).chainId, contracts.bendExchange.address)
);
snapshots.capture("setup");
}
Expand Down
65 changes: 62 additions & 3 deletions test/helpers/order-helper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { MakerOrder, MakerOrderWithSignature, TakerOrder } from "./order-types";
import { signMakerOrder } from "./signature-helper";
import { findPrivateKey } from "./hardhat-keys";

export interface SignedMakerOrder extends MakerOrder {
export interface HardhatMakerOrder extends MakerOrder {
signerUser: SignerWithAddress;
verifyingContract: string;
}

export interface SignedMakerOrder extends MakerOrder {
verifyingContract: string;
privateKey: string;
chainId: number;
}

export async function createMakerOrder({
isOrderAsk,
maker,
Expand All @@ -25,7 +32,7 @@ export async function createMakerOrder({
interceptorExtra,
signerUser,
verifyingContract,
}: SignedMakerOrder): Promise<MakerOrderWithSignature> {
}: HardhatMakerOrder): Promise<MakerOrderWithSignature> {
const makerOrder: MakerOrder = {
isOrderAsk: isOrderAsk,
maker: maker,
Expand All @@ -44,7 +51,59 @@ export async function createMakerOrder({
interceptorExtra,
};

const signedOrder = await signMakerOrder(signerUser, verifyingContract, makerOrder);
const privateKey = findPrivateKey(signerUser.address);
const chainId = 31337;
const signedOrder = await signMakerOrder(chainId, privateKey, verifyingContract, makerOrder);

// Extend makerOrder with proper signature
const makerOrderExtended: MakerOrderWithSignature = {
...makerOrder,
r: signedOrder.r,
s: signedOrder.s,
v: signedOrder.v,
};

return makerOrderExtended;
}

export async function createSignedMakerOrder({
isOrderAsk,
maker,
collection,
price,
tokenId,
amount,
strategy,
currency,
nonce,
startTime,
endTime,
minPercentageToAsk,
params,
interceptor,
interceptorExtra,
privateKey,
chainId,
verifyingContract,
}: SignedMakerOrder): Promise<MakerOrderWithSignature> {
const makerOrder: MakerOrder = {
isOrderAsk: isOrderAsk,
maker: maker,
collection: collection,
price: price,
tokenId: tokenId,
amount: amount,
strategy: strategy,
currency: currency,
nonce: nonce,
startTime: startTime,
endTime: endTime,
minPercentageToAsk: minPercentageToAsk,
params: params,
interceptor,
interceptorExtra,
};
const signedOrder = await signMakerOrder(chainId, privateKey, verifyingContract, makerOrder);

// Extend makerOrder with proper signature
const makerOrderExtended: MakerOrderWithSignature = {
Expand Down
39 changes: 22 additions & 17 deletions test/helpers/signature-helper.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
import { BigNumber, utils, Wallet } from "ethers";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
/* eslint-disable node/no-extraneous-import */
import { TypedDataDomain } from "@ethersproject/abstract-signer";
/* eslint-disable node/no-extraneous-import */
import { Signature } from "@ethersproject/bytes";
/* eslint-disable node/no-extraneous-import */
import { _TypedDataEncoder } from "@ethersproject/hash";
import { MakerOrder } from "./order-types";
import { findPrivateKey } from "./hardhat-keys";

const { defaultAbiCoder, keccak256, solidityPack } = utils;

const BEND_EXCHANGE_NAME = "BendExchange";
const BEND_EXCHANGE_VERSION = "1";

/**
* Generate a signature used to generate v, r, s parameters
* @param signer signer
* @param chainId chainId
* @param privateKey privateKey
* @param types solidity types of the value param
* @param values params to be sent to the Solidity function
* @param verifyingContract verifying contract address ("BendExchange")
* @returns splitted signature
* @see https://docs.ethers.io/v5/api/signer/#Signer-signTypedData
*/
const signTypedData = async (
signer: SignerWithAddress,
chainId: number,
privateKey: string,
types: string[],
values: (string | boolean | BigNumber)[],
verifyingContract: string
): Promise<Signature> => {
const domain: TypedDataDomain = {
name: "BendExchange",
version: "1",
chainId: "31337", // HRE
verifyingContract: verifyingContract,
name: BEND_EXCHANGE_NAME,
version: BEND_EXCHANGE_VERSION,
chainId,
verifyingContract,
};

const domainSeparator = _TypedDataEncoder.hashDomain(domain);
Expand All @@ -43,16 +46,16 @@ const signTypedData = async (
solidityPack(["bytes1", "bytes1", "bytes32", "bytes32"], ["0x19", "0x01", domainSeparator, hash])
);

const adjustedSigner = new Wallet(findPrivateKey(signer.address));
const adjustedSigner = new Wallet(privateKey);
return { ...adjustedSigner._signingKey().signDigest(digest) };
};

export const computeDomainSeparator = (verifyingContract: string): string => {
export const computeDomainSeparator = (chainId: number, verifyingContract: string): string => {
const domain: TypedDataDomain = {
name: "BendExchange",
version: "1",
chainId: "31337", // HRE
verifyingContract: verifyingContract,
name: BEND_EXCHANGE_NAME,
version: BEND_EXCHANGE_VERSION,
chainId,
verifyingContract,
};

return _TypedDataEncoder.hashDomain(domain);
Expand Down Expand Up @@ -106,13 +109,15 @@ export const computeOrderHash = (order: MakerOrder): string => {

/**
* Create a signature for a maker order
* @param signer signer for the order
* @param chainId chainId
* @param privateKey privateKey
* @param verifyingContract verifying contract address
* @param order see MakerOrder definition
* @returns splitted signature
*/
export const signMakerOrder = (
signer: SignerWithAddress,
chainId: number,
privateKey: string,
verifyingContract: string,
order: MakerOrder
): Promise<Signature> => {
Expand Down Expand Up @@ -154,5 +159,5 @@ export const signMakerOrder = (
keccak256(order.interceptorExtra),
];

return signTypedData(signer, types, values, verifyingContract);
return signTypedData(chainId, privateKey, types, values, verifyingContract);
};

0 comments on commit d3c8c8e

Please sign in to comment.