diff --git a/.gitignore b/.gitignore index 0354a2a..e2d7395 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ gas-report.txt typechain-types coverage coverage.json -contracts \ No newline at end of file +contracts +.env \ No newline at end of file diff --git a/scripts/testnet.ts b/scripts/testnet.ts new file mode 100644 index 0000000..9e380a5 --- /dev/null +++ b/scripts/testnet.ts @@ -0,0 +1,105 @@ +import { Wallet } from "ethers" +import { + VaultManager, + LockDealNFT, + DealProvider, + LockDealProvider, + TimedDealProvider, + CollateralProvider, + RefundProvider, + SimpleBuilder, + SimpleRefundBuilder, + ERC20Token, +} from "../typechain-types" +import { deployFrom, setTrustee, approveContracts, createNewVault, approveToken } from "./utility/manageable" +import { createSimpleNFT, createRefundNFT } from "./utility/creation" +import { amount, startTime, finishTime, password, provider } from "./utility/constants" +import { _withdrawPools, _splitPools } from "./utility/control" +import { createMassSimplePools, createMassRefundPools } from "./utility/builders" + +let vaultManager: VaultManager, + lockDealNFT: LockDealNFT, + dealProvider: DealProvider, + lockProvider: LockDealProvider, + timedProvider: TimedDealProvider, + collateralProvider: CollateralProvider, + refundProvider: RefundProvider, + simpleBuilder: SimpleBuilder, + simpleRefundBuilder: SimpleRefundBuilder, + token: ERC20Token, + mainCoin: ERC20Token + +async function main() { + try { + const user = new Wallet(password.toString(), provider) + await deploy(user) + await setup(user) + const ids = await createPools(user) + await splitPools(user, ids) + await withdrawPools(user, ids) + await createMassSimplePools(user, simpleBuilder, vaultManager, dealProvider.address, token) + await createMassRefundPools(user, simpleRefundBuilder, vaultManager, dealProvider.address, token, mainCoin) + } catch (error) { + console.error("Error in main:", error) + } +} + +async function deploy(user: Wallet) { + vaultManager = await deployFrom("VaultManager", user) + lockDealNFT = await deployFrom("LockDealNFT", user, vaultManager.address, "") + dealProvider = await deployFrom("DealProvider", user, lockDealNFT.address) + lockProvider = await deployFrom("LockDealProvider", user, lockDealNFT.address, dealProvider.address) + timedProvider = await deployFrom("TimedDealProvider", user, lockDealNFT.address, lockProvider.address) + collateralProvider = await deployFrom("CollateralProvider", user, lockDealNFT.address, dealProvider.address) + refundProvider = await deployFrom("RefundProvider", user, lockDealNFT.address, collateralProvider.address) + simpleBuilder = await deployFrom("SimpleBuilder", user, lockDealNFT.address) + simpleRefundBuilder = await deployFrom( + "SimpleRefundBuilder", + user, + lockDealNFT.address, + refundProvider.address, + collateralProvider.address + ) + token = await deployFrom("ERC20Token", user, "Test Token", "TT") + mainCoin = await deployFrom("ERC20Token", user, "USDT", "TT") +} + +async function setup(user: Wallet) { + await setTrustee(vaultManager, user, lockDealNFT.address) + await approveContracts(user, lockDealNFT, [ + dealProvider, + lockProvider, + timedProvider, + collateralProvider, + refundProvider, + simpleBuilder, + simpleRefundBuilder, + ]) + await createNewVault(vaultManager, user, token) + await createNewVault(vaultManager, user, mainCoin) + await approveToken(token, user, vaultManager.address) + await approveToken(mainCoin, user, vaultManager.address) + console.log("Setup done") +} + +async function createPools(user: Wallet): Promise { + const id = parseInt((await lockDealNFT.totalSupply()).toString()) + await createSimpleNFT(user, dealProvider, vaultManager, token, [amount]) + await createSimpleNFT(user, lockProvider, vaultManager, token, [amount, startTime]) + await createSimpleNFT(user, timedProvider, vaultManager, token, [amount, startTime, finishTime]) + await createRefundNFT(user, refundProvider, timedProvider, vaultManager, token, mainCoin) + // IDs are always [id, id + 1...] every time the script is run + return [id, id + 1, id + 2, id + 3] +} + +async function splitPools(user: Wallet, ids: number[]) { + await _splitPools(user, lockDealNFT, ids) + console.log("Split NFTs") +} + +async function withdrawPools(user: Wallet, ids: number[]) { + await _withdrawPools(user, lockDealNFT, ids) + console.log("Withdraw NFTs") +} + +main() diff --git a/scripts/utility/builders.ts b/scripts/utility/builders.ts new file mode 100644 index 0000000..6aa3817 --- /dev/null +++ b/scripts/utility/builders.ts @@ -0,0 +1,69 @@ +import { Wallet } from "ethers" +import { gasLimit, gasPrice, amount } from "./constants" +import { SimpleBuilder, SimpleRefundBuilder, ERC20Token, VaultManager } from "../../typechain-types" +import { getSignature } from "./creation" +import { BuilderState } from "../../typechain-types/contracts/LockDealNFT/contracts/Builders/SimpleBuilder/SimpleBuilder" +import { finishTime } from "./constants" + +const sendData: BuilderState.BuilderStruct = { + userPools: [ + { user: "0xf7f1f00b13F4c3D818f052498902067aB91a3A66", amount: amount }, + { user: "0xf7f1f00b13F4c3D818f052498902067aB91a3A66", amount: amount }, + { user: "0xf7f1f00b13F4c3D818f052498902067aB91a3A66", amount: amount }, + { user: "0x6063fBa0fBd645d648C129854Cce45A70dd89691", amount: amount }, + { user: "0x6063fBa0fBd645d648C129854Cce45A70dd89691", amount: amount }, + { user: "0x6063fBa0fBd645d648C129854Cce45A70dd89691", amount: amount }, + { user: "0xf7f1f00b13F4c3D818f052498902067aB91a3A66", amount: amount }, + { user: "0xf7f1f00b13F4c3D818f052498902067aB91a3A66", amount: amount }, + { user: "0xf7f1f00b13F4c3D818f052498902067aB91a3A66", amount: amount }, + ], + totalAmount: amount.mul(9), +} + +export async function createMassSimplePools( + user: Wallet, + simpleBuilder: SimpleBuilder, + vaultManager: VaultManager, + provider: string, + token: ERC20Token +) { + const tx = await simpleBuilder + .connect(user) + .buildMassPools( + [provider, token.address], + sendData, + [], + getSignature(user, vaultManager, token, token.address, amount.mul(9)), + { + gasLimit, + gasPrice, + } + ) + await tx.wait() + console.log("Mass simple NFTs created") +} + +export async function createMassRefundPools( + user: Wallet, + simpleRefundBuilder: SimpleRefundBuilder, + vaultManager: VaultManager, + provider: string, + token: ERC20Token, + mainCoin: ERC20Token +) { + const tx = await simpleRefundBuilder + .connect(user) + .buildMassPools( + [provider, token.address, mainCoin.address], + sendData, + [[amount.mul(3), finishTime], []], + getSignature(user, vaultManager, token, token.address, amount.mul(9)), + getSignature(user, vaultManager, token, mainCoin.address, amount.mul(3)), + { + gasLimit, + gasPrice, + } + ) + await tx.wait() + console.log("Mass refund NFTs created") +} diff --git a/scripts/utility/constants.ts b/scripts/utility/constants.ts new file mode 100644 index 0000000..3900a72 --- /dev/null +++ b/scripts/utility/constants.ts @@ -0,0 +1,14 @@ +import "dotenv/config" +import { BigNumber } from "ethers" +import { ethers } from "hardhat" + +export const gasLimit = 30_000_000 +export const gasPrice = ethers.utils.parseUnits("5", "gwei") +export const unixTime = BigNumber.from(Math.floor(Date.now() / 1000)) +export const week = 60 * 60 * 24 * 7 +export const startTime = unixTime.add(1000) +export const finishTime = unixTime.add(week).mul(2) +export const amount = ethers.utils.parseUnits("100", 8) +export const password = process.env.PASSWORD ?? "" +export const networkRPC = "https://bsc-testnet.publicnode.com" +export const provider = new ethers.providers.JsonRpcProvider(networkRPC) diff --git a/scripts/utility/control.ts b/scripts/utility/control.ts new file mode 100644 index 0000000..8dce3cc --- /dev/null +++ b/scripts/utility/control.ts @@ -0,0 +1,30 @@ +import { Wallet } from "ethers" +import { ethers } from "hardhat" +import { LockDealNFT } from "../../typechain-types" +import { gasLimit, gasPrice } from "./constants" + +export async function _splitPools(user: Wallet, lockDealNFT: LockDealNFT, ids: number[]) { + const ratio = ethers.utils.parseUnits("5", 20) // 50% + const packedData = ethers.utils.defaultAbiCoder.encode(["uint256", "address"], [ratio, user.address]) + for (const id of ids) { + const tx = await lockDealNFT + .connect(user) + ["safeTransferFrom(address,address,uint256,bytes)"](user.address, lockDealNFT.address, id, packedData, { + gasLimit, + gasPrice, + }) + await tx.wait() + } +} + +export async function _withdrawPools(user: Wallet, lockDealNFT: LockDealNFT, ids: number[]) { + for (const id of ids) { + const tx = await lockDealNFT + .connect(user) + ["safeTransferFrom(address,address,uint256)"](user.address, lockDealNFT.address, id, { + gasLimit, + gasPrice, + }) + await tx.wait() + } +} diff --git a/scripts/utility/creation.ts b/scripts/utility/creation.ts new file mode 100644 index 0000000..a831698 --- /dev/null +++ b/scripts/utility/creation.ts @@ -0,0 +1,69 @@ +import { + DealProvider, + LockDealProvider, + TimedDealProvider, + RefundProvider, + VaultManager, + ERC20Token, +} from "../../typechain-types" +import { Wallet, BigNumber } from "ethers" +import { ethers } from "hardhat" +import { gasLimit, gasPrice, startTime, finishTime, amount } from "./constants" + +async function createSimpleNFT( + user: Wallet, + provider: DealProvider | LockDealProvider | TimedDealProvider, + vaultManager: VaultManager, + token: ERC20Token, + poolParams: BigNumber[] +) { + const userAddress: string = await user.address + const tokenSignature = await getSignature(user, vaultManager, token, token.address) + const tx = await provider + .connect(user) + .createNewPool([userAddress, token.address], [...poolParams], tokenSignature, { + gasLimit, + gasPrice, + }) + await tx.wait() + const name = await provider.name() + console.log(name + ` NFT created`) +} + +async function createRefundNFT( + user: Wallet, + refundProvider: RefundProvider, + provider: DealProvider | LockDealProvider | TimedDealProvider, + vaultManager: VaultManager, + token: ERC20Token, + mainCoin: ERC20Token +) { + const name = await refundProvider.name() + const tokenSignature = await getSignature(user, vaultManager, token, token.address) + const mainCoinsignature = await getSignature(user, vaultManager, token, mainCoin.address) + const addresses = [user.address, token.address, mainCoin.address, provider.address] + const params = [amount, startTime, finishTime, amount, finishTime] + const tx = await refundProvider + .connect(user) + .createNewRefundPool(addresses, params, tokenSignature, mainCoinsignature, { gasLimit, gasPrice }) + await tx.wait() + console.log(name + ` NFT created`) +} + +async function getSignature( + user: Wallet, + vaultManager: VaultManager, + token: ERC20Token, + tokenAddress: string = token.address, + tokenAmount: BigNumber = amount +) { + const dataToCheck = ethers.utils.solidityPack(["address", "uint256"], [tokenAddress, tokenAmount]) + const currentNonce = await vaultManager.nonces(user.address) + const hash = ethers.utils.solidityKeccak256( + ["bytes", "uint"], + [dataToCheck, tokenAddress == token.address ? currentNonce : currentNonce.add(1)] + ) + return await user.signMessage(ethers.utils.arrayify(hash)) +} + +export { createSimpleNFT, createRefundNFT, getSignature } diff --git a/scripts/utility/manageable.ts b/scripts/utility/manageable.ts new file mode 100644 index 0000000..a611f50 --- /dev/null +++ b/scripts/utility/manageable.ts @@ -0,0 +1,42 @@ +import { Wallet, constants } from "ethers" +import { ERC20Token, LockDealNFT, VaultManager } from "../../typechain-types" +import { ethers } from "hardhat" +import { gasLimit, gasPrice } from "./constants" + +async function deployFrom(contractName: string, user: Wallet, ...args: string[]): Promise { + const Contract = await ethers.getContractFactory(contractName, user) + const contract = await Contract.connect(user).deploy(...args) + console.log(`Deploying ${contractName}...`) + return contract.deployed() as Promise +} + +async function setTrustee(vaultManager: VaultManager, user: Wallet, address: string) { + const tx = await vaultManager.connect(user).setTrustee(address, { gasLimit, gasPrice }) + await tx.wait() +} + +async function approveContracts(user: Wallet, lockDealNFT: LockDealNFT, contracts: any[]) { + for (const contract of contracts) { + await approveContract(user, lockDealNFT, contract) + } +} + +async function createNewVault(vaultManager: VaultManager, user: Wallet, token: ERC20Token) { + const tx = await vaultManager.connect(user)["createNewVault(address)"](token.address, { gasLimit, gasPrice }) + await tx.wait() +} + +async function approveToken(token: ERC20Token, user: Wallet, spender: string) { + const tx = await token.connect(user).approve(spender, constants.MaxUint256, { gasLimit, gasPrice }) + await tx.wait() +} + +async function approveContract(user: Wallet, lockDealNFT: LockDealNFT, contract: any) { + const tx = await lockDealNFT.connect(user).setApprovedContract(contract.address, true, { + gasLimit: gasLimit, + gasPrice: gasPrice, + }) + await tx.wait() +} + +export { deployFrom, setTrustee, approveContracts, createNewVault, approveToken, approveContract } diff --git a/tsconfig.json b/tsconfig.json index 6fcd4f4..8617ff1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["hardhat", "node"], "target": "es2020", "module": "commonjs", "moduleResolution": "node", @@ -8,5 +9,7 @@ "strict": true, "skipLibCheck": true, "resolveJsonModule": true - } + }, + "include": ["./test", "./src", "./scripts"], + "files": ["./hardhat.config.ts"] }