mirror of
https://github.com/Instadapp/Gelato-automations.git
synced 2024-07-29 22:28:07 +00:00
automatic DSR - CDAI rebalance prototype tested
This commit is contained in:
parent
7ce48fd5ea
commit
3956eab93f
30
.circleci/config.yml
Normal file
30
.circleci/config.yml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
version: 2.1 # use CircleCI 2.1
|
||||||
|
jobs: # a collection of steps
|
||||||
|
build: # runs not using Workflows must have a `build` job as entry point
|
||||||
|
working_directory: ~/gelato-instadapp-ci # directory where steps will run
|
||||||
|
docker: # run the steps with Docker
|
||||||
|
- image: circleci/node:12.16.2 # ...with this image as the primary container; this is where all `steps` will run
|
||||||
|
steps: # a collection of executable commands
|
||||||
|
- checkout # special step to check out source code to working directory
|
||||||
|
- restore_cache: # special step to restore the dependency cache
|
||||||
|
# Read about caching dependencies: https://circleci.com/docs/2.0/caching/
|
||||||
|
name: Restore Yarn Package Cache
|
||||||
|
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||||
|
- run:
|
||||||
|
name: yarn install
|
||||||
|
command: yarn install --frozen-lockfile
|
||||||
|
- save_cache: # special step to save the dependency cache
|
||||||
|
name: Save Yarn Package Cache
|
||||||
|
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||||
|
paths:
|
||||||
|
- ./node_modules
|
||||||
|
- run: # Compile
|
||||||
|
name: Compile
|
||||||
|
command: npx buidler compile
|
||||||
|
- run: # Tests
|
||||||
|
name: Tests using buidler-ganache mainnet fork
|
||||||
|
command: npx buidler test
|
||||||
|
# - store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
|
||||||
|
# path: coverage
|
||||||
|
# prefix: coverage
|
||||||
|
# See https://circleci.com/docs/2.0/deployment-integrations/ for deploy examples
|
27
README.md
27
README.md
|
@ -1 +1,28 @@
|
||||||
# gelato-instadapp
|
# gelato-instadapp
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="assets/instadapp_filled.svg" width="150px"/>
|
||||||
|
<img src="assets/Gelato_Black.svg" width="150px"/></p>
|
||||||
|
|
||||||
|
This repo contains smart contract prototypes and mocks and a test suite showcasing how the InstaDapp DSA could use Gelato to automate the execution (or casting) of its Spells (connectors) based on arbitrary Conditions.
|
||||||
|
|
||||||
|
For the first iteration, we started with a simple spell:
|
||||||
|
**Move DAI lending from DSR to Compound.**
|
||||||
|
This reused two existing deployed InstaDapp Connectors: `ConnectMaker.withdrawDai` and `ConnectCompound.deposit`.
|
||||||
|
|
||||||
|
Furtheremore the following contracts were added to showcase the automation of the Spell:
|
||||||
|
|
||||||
|
- `MockCDAI.sol` and `MockDSR.sol`: to normalize CDAI.supplyRatePerBlock and dsr values to a _per second rate in 10\*\*27 precision_
|
||||||
|
|
||||||
|
- `ConditionCompareUintsFromTwoSource`: a generic Gelato Condition that allows you to read and compare data from 2 arbitrary on-chain sources (returndata expected to be uint256 and normalized => hence MockDSR and MockCDAI). This Condition was used to compare DSR to CDAI rates and in the test suite we showcase how a change in the CDAI rate (it going above the DSR) can trigger an automatic rebalancing from DSR to CDAI via DSA Connectors.
|
||||||
|
|
||||||
|
- `ProviderModuleDSA`: this is needed for any Gelato integration. It tells Gelato how the execution payload should be formatted. In this prototype, it formats the payload for the `DSA.cast` function.
|
||||||
|
|
||||||
|
- `ConnectGelato`: this is a Connector needed for the DSA to be able to submit Tasks to Gelato. In the test suite we unlock the DSA MultiSig Master account at 0xfCD22438AD6eD564a1C26151Df73F6B33B817B56, in order to be able to enable this Connector in our mainnet fork running on the local ganache instance.
|
||||||
|
|
||||||
|
To see for yourself check out the [contracts](./contracts) folder and make sure to check out `test/mv-DAI-DSR-Compound.test.js`, to see an end-to-end test showcasing the prototype. To do so follow the steps below:
|
||||||
|
|
||||||
|
1. Clone this repo
|
||||||
|
2. Put your Infura ID in .env
|
||||||
|
3. yarn install
|
||||||
|
4. npx buidler test
|
||||||
|
|
5450
assets/Gelato_Black.svg
Normal file
5450
assets/Gelato_Black.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 398 KiB |
1
assets/instadapp_filled.svg
Normal file
1
assets/instadapp_filled.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.3 KiB |
|
@ -1,389 +0,0 @@
|
||||||
// We require the Buidler Runtime Environment explicitly here. This is optional
|
|
||||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
|
||||||
// When running the script with `buidler run <script>` you'll find the Buidler
|
|
||||||
// Runtime Environment's members available in the global scope.
|
|
||||||
const bre = require("@nomiclabs/buidler");
|
|
||||||
const ethers = bre.ethers;
|
|
||||||
const { constants, utils } = require("ethers");
|
|
||||||
|
|
||||||
// CPK Library
|
|
||||||
const CPK = require("contract-proxy-kit-custom");
|
|
||||||
|
|
||||||
// running `npx buidler test` automatically makes use of buidler-waffle plugin
|
|
||||||
// => only dependency we need is "chaFi"
|
|
||||||
const { expect } = require("chai");
|
|
||||||
|
|
||||||
const GelatoCoreLib = require("@gelatonetwork/core");
|
|
||||||
|
|
||||||
const GELATO = bre.network.config.deployments.GelatoCore;
|
|
||||||
const EXECUTOR = bre.network.config.addressBook.gelatoExecutor.default;
|
|
||||||
const PROVIDER_MODULE_GNOSIS =
|
|
||||||
bre.network.config.deployments.ProviderModuleGnosisSafeProxy;
|
|
||||||
|
|
||||||
// The gas limit for our automated CHI.mint TX
|
|
||||||
// ActionChiMint caps chiAmount to 140 CHI => 6 mio gas should always suffice
|
|
||||||
const CHI_TOKENS_TO_MINT = 10; // should be kept below 140 MAX!
|
|
||||||
const PER_CHI_GAS_EST = 50000;
|
|
||||||
const GELATO_OVERHEAD = 200000;
|
|
||||||
const SELF_PROVIDER_GAS_LIMIT = utils.bigNumberify(
|
|
||||||
PER_CHI_GAS_EST * CHI_TOKENS_TO_MINT + GELATO_OVERHEAD
|
|
||||||
);
|
|
||||||
|
|
||||||
// Current Gelato Gas Price
|
|
||||||
let currentGelatoGasPrice;
|
|
||||||
|
|
||||||
// TRIGGER GAS PRICE
|
|
||||||
let triggerGasPrice;
|
|
||||||
|
|
||||||
// FUNDS TO DEPOSIT
|
|
||||||
let fundsToDeposit = 0;
|
|
||||||
|
|
||||||
describe("1-click anything for auto-minting CHI", function () {
|
|
||||||
// No timeout for Mocha due to Rinkeby mining latency
|
|
||||||
this.timeout(0);
|
|
||||||
|
|
||||||
// We use our User Wallet. Per our config this wallet is at the accounts index 0
|
|
||||||
// and hence will be used by default for all transactions we send.
|
|
||||||
let myUserWallet;
|
|
||||||
let myUserAddress;
|
|
||||||
|
|
||||||
// 2) We will deploy a GnosisSafeProxy using the Factory, or if we already deployed
|
|
||||||
// one, we will use that one.
|
|
||||||
let cpk;
|
|
||||||
let gnosisSafe;
|
|
||||||
let proxyIsDeployed;
|
|
||||||
|
|
||||||
let gelatoCore;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
// We get our User Wallet from the Buidler Runtime Env
|
|
||||||
[myUserWallet] = await bre.ethers.getSigners();
|
|
||||||
myUserAddress = await myUserWallet.getAddress();
|
|
||||||
|
|
||||||
// Create CPK instance connected to new mastercopy
|
|
||||||
cpk = await CPK.create({ ethers, signer: myUserWallet });
|
|
||||||
expect(await cpk.getOwnerAccount()).to.be.equal(myUserAddress);
|
|
||||||
|
|
||||||
const codeAtProxy = await bre.ethers.provider.getCode(cpk.address);
|
|
||||||
proxyIsDeployed = codeAtProxy === "0x" ? false : true;
|
|
||||||
|
|
||||||
if (proxyIsDeployed) {
|
|
||||||
gnosisSafe = await bre.ethers.getContractAt(
|
|
||||||
bre.GnosisSafe.abi,
|
|
||||||
cpk.address
|
|
||||||
);
|
|
||||||
expect(await gnosisSafe.isOwner(myUserAddress)).to.be.true;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
\n Network: ${bre.network.name}\
|
|
||||||
\n CPK Proxy address: ${cpk.address}\
|
|
||||||
\n Proxy deployed?: ${proxyIsDeployed}\n
|
|
||||||
`);
|
|
||||||
|
|
||||||
gelatoCore = await ethers.getContractAt(
|
|
||||||
GelatoCoreLib.GelatoCore.abi,
|
|
||||||
network.config.deployments.GelatoCore // the Rinkeby Address of the deployed GelatoCore
|
|
||||||
);
|
|
||||||
|
|
||||||
currentGelatoGasPrice = await bre.run("fetchGelatoGasPrice");
|
|
||||||
|
|
||||||
// FOR TESTING WE SET IT EQUAL TO CURRENT SO WE CAN CHECK FOR EXECUTION
|
|
||||||
triggerGasPrice = currentGelatoGasPrice;
|
|
||||||
|
|
||||||
// FUNDS TO DEPOSIT
|
|
||||||
fundsToDeposit = await gelatoCore.minExecProviderFunds(
|
|
||||||
SELF_PROVIDER_GAS_LIMIT,
|
|
||||||
triggerGasPrice
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("In a single tx: [deployProxy], whitelist GnosisModule, setup Gelato, submitTask", async function () {
|
|
||||||
// Check if Gelato is already whitelisted as Safe Module
|
|
||||||
let gelatoIsWhitelisted = false;
|
|
||||||
if (proxyIsDeployed)
|
|
||||||
gelatoIsWhitelisted = await gnosisSafe.isModuleEnabled(GELATO);
|
|
||||||
if (gelatoIsWhitelisted === true)
|
|
||||||
console.log(`✅ Gelato Safe Module ALREADY whitelisted.`);
|
|
||||||
|
|
||||||
// Check current funding on Gelato
|
|
||||||
const currentProviderFunds = await gelatoCore.providerFunds(cpk.address);
|
|
||||||
const fundsAlreadyProvided = currentProviderFunds.gte(fundsToDeposit);
|
|
||||||
if (fundsAlreadyProvided) {
|
|
||||||
console.log(
|
|
||||||
`\n ✅ Already provided ${utils.formatEther(
|
|
||||||
currentProviderFunds
|
|
||||||
)} ETH on Gelato`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if SelfProvider UserProxy already has Default Executor assigned
|
|
||||||
const assignedExecutor = await gelatoCore.executorByProvider(
|
|
||||||
cpk.address // As the User is being his own provider, we will use the userProxy's address as the provider address
|
|
||||||
);
|
|
||||||
const isDefaultExecutorAssigned =
|
|
||||||
utils.getAddress(assignedExecutor) === utils.getAddress(EXECUTOR)
|
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
if (isDefaultExecutorAssigned)
|
|
||||||
console.log("\n ✅Default Executor ALREADY assigned");
|
|
||||||
|
|
||||||
const isExecutorValid = await gelatoCore.isExecutorMinStaked(EXECUTOR);
|
|
||||||
if (!isExecutorValid) {
|
|
||||||
console.error("❌ Executor is not minStaked");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user wants to use Gelato through their GnosisSafe,
|
|
||||||
// he needs to register the ProviderModuleGnosisSafeProxy
|
|
||||||
// to make his GnosisSafe compatible with Gelato. Here we check,
|
|
||||||
// if the User already enabled the ProviderModuleGnosisSafeProxy.
|
|
||||||
// If not, we will enable it in the upcoming Tx.
|
|
||||||
const isUserProxyModuleWhitelisted = await gelatoCore.isModuleProvided(
|
|
||||||
cpk.address,
|
|
||||||
PROVIDER_MODULE_GNOSIS
|
|
||||||
);
|
|
||||||
if (isUserProxyModuleWhitelisted)
|
|
||||||
console.log("\n ✅ UserProxyModule ALREADY whitelisted");
|
|
||||||
|
|
||||||
// To submit Tasks to Gelato we need to instantiate a GelatoProvider object
|
|
||||||
const myGelatoProvider = new GelatoCoreLib.GelatoProvider({
|
|
||||||
addr: cpk.address, // This time, the provider is paying for the Task, hence we input the Providers address
|
|
||||||
module: PROVIDER_MODULE_GNOSIS,
|
|
||||||
});
|
|
||||||
|
|
||||||
let actionChiMint = await deployments.get("ActionChiMint");
|
|
||||||
actionChiMint = await bre.ethers.getContractAt(
|
|
||||||
actionChiMint.abi,
|
|
||||||
actionChiMint.address
|
|
||||||
);
|
|
||||||
|
|
||||||
// Specify and Instantiate the Gelato Task
|
|
||||||
const taskAutoMintCHIWhenTriggerGasPrice = new GelatoCoreLib.Task({
|
|
||||||
actions: [
|
|
||||||
new GelatoCoreLib.Action({
|
|
||||||
addr: actionChiMint.address,
|
|
||||||
data: await actionChiMint.getActionData(
|
|
||||||
myUserAddress, // recipient of CHI Tokens
|
|
||||||
CHI_TOKENS_TO_MINT // CHI Tokens to be minted
|
|
||||||
),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
termsOkCheck: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
selfProviderGasLimit: SELF_PROVIDER_GAS_LIMIT,
|
|
||||||
// This makes sure we only mint CHI when the gelatoGasPrice is at or below
|
|
||||||
// our desired trigger gas price
|
|
||||||
selfProviderGasPriceCeil: triggerGasPrice,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Specify ExpiryDate: 0 for infinite validity
|
|
||||||
const EXPIRY_DATE = 0;
|
|
||||||
|
|
||||||
// The single Transaction that:
|
|
||||||
// 1) deploys a GnosisSafeProxy if not deployed
|
|
||||||
// 2) enableModule(GELATO on GnosisSafe
|
|
||||||
// 3) multiProvide(funds, executor, providerModuleGnosisSafeProxy) on Gelato
|
|
||||||
// 4) submitTask to GELATO
|
|
||||||
|
|
||||||
try {
|
|
||||||
let tx;
|
|
||||||
if (!gelatoIsWhitelisted) {
|
|
||||||
// If we have not enabled Gelato Module we enable it and then setup Gelato
|
|
||||||
// and submitTask
|
|
||||||
console.log(
|
|
||||||
"\n Sending TX to whitelist Gelato Gnosis Module, setup UserProxy and submitTask"
|
|
||||||
);
|
|
||||||
tx = await cpk.execTransactions(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
to: cpk.address,
|
|
||||||
operation: CPK.CALL,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: bre.GnosisSafe.abi,
|
|
||||||
functionname: "enableModule",
|
|
||||||
inputs: [GELATO],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
to: GELATO,
|
|
||||||
operation: CPK.CALL,
|
|
||||||
value: fundsAlreadyProvided ? 0 : fundsToDeposit,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "multiProvide",
|
|
||||||
inputs: [
|
|
||||||
isDefaultExecutorAssigned ? constants.AddressZero : EXECUTOR,
|
|
||||||
[], // this can be left empty, as it is only relevant for external providers
|
|
||||||
isUserProxyModuleWhitelisted ? [] : [PROVIDER_MODULE_GNOSIS],
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "submitTask",
|
|
||||||
inputs: [
|
|
||||||
myGelatoProvider,
|
|
||||||
taskAutoMintCHIWhenTriggerGasPrice,
|
|
||||||
EXPIRY_DATE,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
value: fundsAlreadyProvided ? 0 : fundsToDeposit,
|
|
||||||
gasLimit: 5000000,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
!fundsAlreadyProvided ||
|
|
||||||
!isDefaultExecutorAssigned ||
|
|
||||||
!isUserProxyModuleWhitelisted
|
|
||||||
) {
|
|
||||||
// If we already enabled Gelato Module we only setup Gelato and submitTask
|
|
||||||
console.log("\n Sending TX to setup UserProxy and submitTask");
|
|
||||||
|
|
||||||
tx = await cpk.execTransactions(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
to: GELATO,
|
|
||||||
operation: CPK.CALL,
|
|
||||||
value: fundsAlreadyProvided ? 0 : fundsToDeposit,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "multiProvide",
|
|
||||||
inputs: [
|
|
||||||
isDefaultExecutorAssigned ? constants.AddressZero : EXECUTOR,
|
|
||||||
[], // this can be left empty, as it is only relevant for external providers
|
|
||||||
isUserProxyModuleWhitelisted ? [] : [PROVIDER_MODULE_GNOSIS],
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "submitTask",
|
|
||||||
inputs: [
|
|
||||||
myGelatoProvider,
|
|
||||||
taskAutoMintCHIWhenTriggerGasPrice,
|
|
||||||
EXPIRY_DATE,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
value: fundsAlreadyProvided ? 0 : fundsToDeposit,
|
|
||||||
gasLimit: 5000000,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// If we already enabled Gelato Module and already setup Gelato
|
|
||||||
console.log("\n Sending TX to submitTask");
|
|
||||||
|
|
||||||
tx = await cpk.execTransactions(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "submitTask",
|
|
||||||
inputs: [
|
|
||||||
myGelatoProvider,
|
|
||||||
taskAutoMintCHIWhenTriggerGasPrice,
|
|
||||||
EXPIRY_DATE,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
value: fundsAlreadyProvided ? 0 : fundsToDeposit,
|
|
||||||
gasLimit: 5000000,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for mining
|
|
||||||
console.log("📓 all-in-one TX:", tx.hash);
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
|
|
||||||
// Mined !
|
|
||||||
// Make sure User is owner of deployed GnosisSafe
|
|
||||||
gnosisSafe = await bre.ethers.getContractAt(
|
|
||||||
bre.GnosisSafe.abi,
|
|
||||||
cpk.address
|
|
||||||
);
|
|
||||||
expect(await gnosisSafe.isOwner(myUserAddress)).to.be.true;
|
|
||||||
|
|
||||||
// GelatoModule whitelisted on GnosisSafe
|
|
||||||
if (!gelatoIsWhitelisted) {
|
|
||||||
expect(await gnosisSafe.isModuleEnabled(GELATO)).to.be.true;
|
|
||||||
console.log(`✅ Tx: Gelato GnosisModule whitelisted.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provided Funds on Gelato
|
|
||||||
if (!fundsAlreadyProvided) {
|
|
||||||
expect(await gelatoCore.providerFunds(gnosisSafe.address)).to.be.gte(
|
|
||||||
fundsToDeposit
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`✅ Tx: Deposited ${utils.formatEther(fundsToDeposit)} ETH on gelato`
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`Funds on Gelato: ${utils.formatEther(
|
|
||||||
await gelatoCore.providerFunds(gnosisSafe.address)
|
|
||||||
)} ETH`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gelato Default Executor assigned
|
|
||||||
if (!isDefaultExecutorAssigned) {
|
|
||||||
expect(
|
|
||||||
await gelatoCore.executorByProvider(gnosisSafe.address)
|
|
||||||
).to.be.equal(EXECUTOR);
|
|
||||||
console.log(`✅ Tx: Selected default execution network: ${EXECUTOR}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProviderModuleGnosisSafeProxy whitelisted on Gelato
|
|
||||||
if (!isUserProxyModuleWhitelisted) {
|
|
||||||
expect(
|
|
||||||
await gelatoCore.isModuleProvided(
|
|
||||||
gnosisSafe.address,
|
|
||||||
PROVIDER_MODULE_GNOSIS
|
|
||||||
)
|
|
||||||
).to.be.true;
|
|
||||||
console.log(
|
|
||||||
`✅ Tx: Whitelisted ProviderModuleGnosisSafeProxy: ${PROVIDER_MODULE_GNOSIS}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For our Task to be executable, our Provider must have sufficient funds on Gelato
|
|
||||||
const providerIsLiquid = await gelatoCore.isProviderLiquid(
|
|
||||||
cpk.address,
|
|
||||||
SELF_PROVIDER_GAS_LIMIT, // we need roughtly estimatedGasPerExecution * 3 executions as balance on gelato
|
|
||||||
triggerGasPrice
|
|
||||||
);
|
|
||||||
if (!providerIsLiquid) {
|
|
||||||
console.log(
|
|
||||||
"\n ❌ Ooops! Your GnosisSafe needs to provide more funds to Gelato \n"
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SUCCESS !
|
|
||||||
console.log("\nUser Proxy succesfully set up and Task Submitted ✅ \n");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n Gelato UserProxy Setup Error ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,264 +0,0 @@
|
||||||
// We require the Buidler Runtime Environment explicitly here. This is optional
|
|
||||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
|
||||||
// When running the script with `buidler run <script>` you'll find the Buidler
|
|
||||||
// Runtime Environment's members available in the global scope.
|
|
||||||
const bre = require("@nomiclabs/buidler");
|
|
||||||
const ethers = bre.ethers;
|
|
||||||
const { constants, utils } = require("ethers");
|
|
||||||
|
|
||||||
// CPK Library
|
|
||||||
const CPK = require("contract-proxy-kit-custom");
|
|
||||||
|
|
||||||
// running `npx buidler test` automatically makes use of buidler-waffle plugin
|
|
||||||
// => only dependency we need is "chaFi"
|
|
||||||
const { expect } = require("chai");
|
|
||||||
|
|
||||||
const GelatoCoreLib = require("@gelatonetwork/core");
|
|
||||||
|
|
||||||
const GELATO = bre.network.config.deployments.GelatoCore;
|
|
||||||
const EXECUTOR = bre.network.config.addressBook.gelatoExecutor.default;
|
|
||||||
const PROVIDER_MODULE_GNOSIS =
|
|
||||||
bre.network.config.deployments.ProviderModuleGnosisSafeProxy;
|
|
||||||
|
|
||||||
const FUNDS_TO_DEPOSIT = utils.parseEther("1");
|
|
||||||
|
|
||||||
describe("Create a GnosisSafe via CPK and setup with Gelato", function () {
|
|
||||||
// No timeout for Mocha due to Rinkeby mining latency
|
|
||||||
this.timeout(0);
|
|
||||||
|
|
||||||
// We use our User Wallet. Per our config this wallet is at the accounts index 0
|
|
||||||
// and hence will be used by default for all transactions we send.
|
|
||||||
let myUserWallet;
|
|
||||||
let myUserAddress;
|
|
||||||
|
|
||||||
// 2) We will deploy a GnosisSafeProxy using the Factory, or if we already deployed
|
|
||||||
// one, we will use that one.
|
|
||||||
let cpk;
|
|
||||||
let gnosisSafe;
|
|
||||||
|
|
||||||
let proxyIsDeployed;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
// We get our User Wallet from the Buidler Runtime Env
|
|
||||||
[myUserWallet] = await bre.ethers.getSigners();
|
|
||||||
myUserAddress = await myUserWallet.getAddress();
|
|
||||||
|
|
||||||
// Create CPK instance connected to new mastercopy
|
|
||||||
cpk = await CPK.create({ ethers, signer: myUserWallet });
|
|
||||||
expect(await cpk.getOwnerAccount()).to.be.equal(myUserAddress);
|
|
||||||
|
|
||||||
const codeAtProxy = await bre.ethers.provider.getCode(cpk.address);
|
|
||||||
proxyIsDeployed = codeAtProxy === "0x" ? false : true;
|
|
||||||
|
|
||||||
if (proxyIsDeployed) {
|
|
||||||
gnosisSafe = await bre.ethers.getContractAt(
|
|
||||||
bre.GnosisSafe.abi,
|
|
||||||
cpk.address
|
|
||||||
);
|
|
||||||
expect(await gnosisSafe.isOwner(myUserAddress)).to.be.true;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
\n Network: ${bre.network.name}\
|
|
||||||
\n CPK Proxy address: ${cpk.address}\
|
|
||||||
\n Proxy deployed?: ${proxyIsDeployed}\n
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Gelato: Whitelist GnosisModule and setup (funds, executor, ProviderModule)", async function () {
|
|
||||||
// Check if Gelato is already whitelisted as Safe Module
|
|
||||||
let gelatoIsWhitelisted = false;
|
|
||||||
if (proxyIsDeployed)
|
|
||||||
gelatoIsWhitelisted = await gnosisSafe.isModuleEnabled(GELATO);
|
|
||||||
if (gelatoIsWhitelisted === true)
|
|
||||||
console.log(`✅ Gelato Safe Module ALREADY whitelisted.`);
|
|
||||||
|
|
||||||
// Instantiate GelatoCore contract instance for sanity checks
|
|
||||||
const gelatoCore = await ethers.getContractAt(
|
|
||||||
GelatoCoreLib.GelatoCore.abi,
|
|
||||||
network.config.deployments.GelatoCore // the Rinkeby Address of the deployed GelatoCore
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check current funding on Gelato
|
|
||||||
const currentProviderFunds = await gelatoCore.providerFunds(cpk.address);
|
|
||||||
const fundsAlreadyProvided = currentProviderFunds.gte(FUNDS_TO_DEPOSIT);
|
|
||||||
if (fundsAlreadyProvided) {
|
|
||||||
console.log(
|
|
||||||
`\n ✅ Already provided ${utils.formatEther(
|
|
||||||
currentProviderFunds
|
|
||||||
)} ETH on Gelato`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if SelfProvider UserProxy already has Default Executor assigned
|
|
||||||
const assignedExecutor = await gelatoCore.executorByProvider(
|
|
||||||
cpk.address // As the User is being his own provider, we will use the userProxy's address as the provider address
|
|
||||||
);
|
|
||||||
const isDefaultExecutorAssigned =
|
|
||||||
utils.getAddress(assignedExecutor) === utils.getAddress(EXECUTOR)
|
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
if (isDefaultExecutorAssigned)
|
|
||||||
console.log("\n ✅Default Executor ALREADY assigned");
|
|
||||||
|
|
||||||
const isExecutorValid = await gelatoCore.isExecutorMinStaked(EXECUTOR);
|
|
||||||
if (!isExecutorValid) {
|
|
||||||
console.error("❌ Executor is not minStaked");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user wants to use Gelato through their GnosisSafe,
|
|
||||||
// he needs to register the ProviderModuleGnosisSafeProxy
|
|
||||||
// to make his GnosisSafe compatible with Gelato. Here we check,
|
|
||||||
// if the User already enabled the ProviderModuleGnosisSafeProxy.
|
|
||||||
// If not, we will enable it in the upcoming Tx.
|
|
||||||
const isUserProxyModuleWhitelisted = await gelatoCore.isModuleProvided(
|
|
||||||
cpk.address,
|
|
||||||
PROVIDER_MODULE_GNOSIS
|
|
||||||
);
|
|
||||||
if (isUserProxyModuleWhitelisted)
|
|
||||||
console.log("\n ✅ UserProxyModule ALREADY whitelisted");
|
|
||||||
|
|
||||||
// The single Transaction that:
|
|
||||||
// 1) deploys a GnosisSafeProxy if not deployed
|
|
||||||
// 2) enableModule(GELATO on GnosisSafe
|
|
||||||
// 3) multiProvide(funds, executor, providerModuleGnosisSafeProxy) on Gelato
|
|
||||||
if (
|
|
||||||
!gelatoIsWhitelisted ||
|
|
||||||
!fundsAlreadyProvided ||
|
|
||||||
!isDefaultExecutorAssigned ||
|
|
||||||
!isUserProxyModuleWhitelisted
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
console.log("\n Sending Transaction to setup UserProxy");
|
|
||||||
|
|
||||||
let tx;
|
|
||||||
if (!gelatoIsWhitelisted) {
|
|
||||||
// If we have not enabled Gelato Module we enable it and then setup Gelato
|
|
||||||
tx = await cpk.execTransactions(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
to: cpk.address,
|
|
||||||
operation: CPK.CALL,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: bre.GnosisSafe.abi,
|
|
||||||
functionname: "enableModule",
|
|
||||||
inputs: [GELATO],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
to: GELATO,
|
|
||||||
operation: CPK.CALL,
|
|
||||||
value: fundsAlreadyProvided ? 0 : FUNDS_TO_DEPOSIT,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "multiProvide",
|
|
||||||
inputs: [
|
|
||||||
isDefaultExecutorAssigned
|
|
||||||
? constants.AddressZero
|
|
||||||
: EXECUTOR,
|
|
||||||
[], // this can be left empty, as it is only relevant for external providers
|
|
||||||
isUserProxyModuleWhitelisted
|
|
||||||
? []
|
|
||||||
: [PROVIDER_MODULE_GNOSIS],
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
value: fundsAlreadyProvided ? 0 : FUNDS_TO_DEPOSIT,
|
|
||||||
gasLimit: 5000000,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// If we already enabled Gelato Module we only setup Gelato
|
|
||||||
tx = await cpk.execTransactions(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
to: GELATO,
|
|
||||||
operation: CPK.CALL,
|
|
||||||
value: fundsAlreadyProvided ? 0 : FUNDS_TO_DEPOSIT,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "multiProvide",
|
|
||||||
inputs: [
|
|
||||||
isDefaultExecutorAssigned
|
|
||||||
? constants.AddressZero
|
|
||||||
: EXECUTOR,
|
|
||||||
[], // this can be left empty, as it is only relevant for external providers
|
|
||||||
isUserProxyModuleWhitelisted
|
|
||||||
? []
|
|
||||||
: [PROVIDER_MODULE_GNOSIS],
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
value: fundsAlreadyProvided ? 0 : FUNDS_TO_DEPOSIT,
|
|
||||||
gasLimit: 5000000,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for mining
|
|
||||||
console.log("TX:", tx.hash);
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
|
|
||||||
// Mined !
|
|
||||||
// Make sure User is owner of deployed GnosisSafe
|
|
||||||
gnosisSafe = await bre.ethers.getContractAt(
|
|
||||||
bre.GnosisSafe.abi,
|
|
||||||
cpk.address
|
|
||||||
);
|
|
||||||
expect(await gnosisSafe.isOwner(myUserAddress)).to.be.true;
|
|
||||||
|
|
||||||
// GelatoModule whitelisted on GnosisSafe
|
|
||||||
expect(await gnosisSafe.isModuleEnabled(GELATO)).to.be.true;
|
|
||||||
console.log(`✅ Gelato GnosisModule whitelisted.`);
|
|
||||||
|
|
||||||
// Provided Funds on Gelato
|
|
||||||
expect(await gelatoCore.providerFunds(gnosisSafe.address)).to.be.gte(
|
|
||||||
FUNDS_TO_DEPOSIT
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`✅ Deposited ${utils.formatEther(FUNDS_TO_DEPOSIT)} ETH on gelato`
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`Funds on Gelato: ${utils.formatEther(
|
|
||||||
await gelatoCore.providerFunds(gnosisSafe.address)
|
|
||||||
)} ETH`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Gelato Default Executor assigned
|
|
||||||
if (!isDefaultExecutorAssigned) {
|
|
||||||
expect(
|
|
||||||
await gelatoCore.executorByProvider(gnosisSafe.address)
|
|
||||||
).to.be.equal(EXECUTOR);
|
|
||||||
console.log(`✅ Selected default execution network: ${EXECUTOR}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProviderModuleGnosisSafeProxy whitelisted on Gelato
|
|
||||||
if (!isUserProxyModuleWhitelisted) {
|
|
||||||
expect(
|
|
||||||
await gelatoCore.isModuleProvided(
|
|
||||||
gnosisSafe.address,
|
|
||||||
PROVIDER_MODULE_GNOSIS
|
|
||||||
)
|
|
||||||
).to.be.true;
|
|
||||||
console.log(
|
|
||||||
`✅ Whitelisted ProviderModuleGnosisSafeProxy: ${PROVIDER_MODULE_GNOSIS}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SUCCESS !
|
|
||||||
console.log("\nUser Proxy succesfully set up ✅ \n");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n Gelato UserProxy Setup Error ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("\n✅ UserProxy ALREADY fully set up on Gelato \n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,197 +0,0 @@
|
||||||
// We require the Buidler Runtime Environment explicitly here. This is optional
|
|
||||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
|
||||||
// When running the script with `buidler run <script>` you'll find the Buidler
|
|
||||||
// Runtime Environment's members available in the global scope.
|
|
||||||
const bre = require("@nomiclabs/buidler");
|
|
||||||
const ethers = bre.ethers;
|
|
||||||
const { utils } = require("ethers");
|
|
||||||
|
|
||||||
// CPK Library
|
|
||||||
const CPK = require("contract-proxy-kit-custom");
|
|
||||||
|
|
||||||
// running `npx buidler test` automatically makes use of buidler-waffle plugin
|
|
||||||
// => only dependency we need is "chaFi"
|
|
||||||
const { expect } = require("chai");
|
|
||||||
|
|
||||||
const GelatoCoreLib = require("@gelatonetwork/core");
|
|
||||||
|
|
||||||
const GELATO = bre.network.config.deployments.GelatoCore;
|
|
||||||
const EXECUTOR = bre.network.config.addressBook.gelatoExecutor.default;
|
|
||||||
const PROVIDER_MODULE_GNOSIS =
|
|
||||||
bre.network.config.deployments.ProviderModuleGnosisSafeProxy;
|
|
||||||
|
|
||||||
// The gas limit for our automated CHI.mint TX
|
|
||||||
// ActionChiMint caps chiAmount to 140 CHI => 6 mio gas should always suffice
|
|
||||||
const CHI_TOKENS_TO_MINT = 10; // should be kept below 140 MAX!
|
|
||||||
const PER_CHI_GAS_EST = 50000;
|
|
||||||
const GELATO_OVERHEAD = 200000;
|
|
||||||
const SELF_PROVIDER_GAS_LIMIT = utils.bigNumberify(
|
|
||||||
PER_CHI_GAS_EST * CHI_TOKENS_TO_MINT + GELATO_OVERHEAD
|
|
||||||
);
|
|
||||||
|
|
||||||
// Current Gelato Gas Price
|
|
||||||
let currentGelatoGasPrice;
|
|
||||||
|
|
||||||
// TRIGGER GAS PRICE
|
|
||||||
let triggerGasPrice;
|
|
||||||
|
|
||||||
describe("Submitting ActionCHIMint Task to Gelato via GnosisSafe", function () {
|
|
||||||
// No timeout for Mocha due to Rinkeby mining latency
|
|
||||||
this.timeout(0);
|
|
||||||
|
|
||||||
// We use our User Wallet. Per our config this wallet is at the accounts index 0
|
|
||||||
// and hence will be used by default for all transactions we send.
|
|
||||||
let myUserWallet;
|
|
||||||
let myUserAddress;
|
|
||||||
|
|
||||||
// 2) We will deploy a GnosisSafeProxy using the Factory, or if we already deployed
|
|
||||||
// one, we will use that one.
|
|
||||||
let cpk;
|
|
||||||
|
|
||||||
let gelatoCore;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
// We get our User Wallet from the Buidler Runtime Env
|
|
||||||
[myUserWallet] = await bre.ethers.getSigners();
|
|
||||||
myUserAddress = await myUserWallet.getAddress();
|
|
||||||
|
|
||||||
// Create CPK instance connected to new mastercopy
|
|
||||||
cpk = await CPK.create({ ethers, signer: myUserWallet });
|
|
||||||
expect(await cpk.getOwnerAccount()).to.be.equal(myUserAddress);
|
|
||||||
|
|
||||||
const codeAtProxy = await bre.ethers.provider.getCode(cpk.address);
|
|
||||||
const proxyDeployed = codeAtProxy === "0x" ? false : true;
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
\n Network: ${bre.network.name}\
|
|
||||||
\n CPK Proxy address: ${cpk.address}\
|
|
||||||
\n Proxy deployed?: ${proxyDeployed}\n
|
|
||||||
`);
|
|
||||||
|
|
||||||
if (proxyDeployed === false) {
|
|
||||||
console.error("Need `yarn setup-proxy` first");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
gelatoCore = await ethers.getContractAt(
|
|
||||||
GelatoCoreLib.GelatoCore.abi,
|
|
||||||
network.config.deployments.GelatoCore // the Rinkeby Address of the deployed GelatoCore
|
|
||||||
);
|
|
||||||
|
|
||||||
currentGelatoGasPrice = await bre.run("fetchGelatoGasPrice");
|
|
||||||
console.log(`Current Price: ${currentGelatoGasPrice.toString()}`);
|
|
||||||
|
|
||||||
triggerGasPrice = currentGelatoGasPrice.sub(utils.parseUnits("4", "Gwei"));
|
|
||||||
console.log(`Trigger Price: ${triggerGasPrice.toString()}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Submit your Task to Gelato via your GelatoUserProxy
|
|
||||||
it("User submits Task as SelfProvider", async function () {
|
|
||||||
// First we want to make sure that the Task we want to submit actually has
|
|
||||||
// a valid Provider, so we need to ask GelatoCore some questions about the Provider.
|
|
||||||
|
|
||||||
// For our Task to be executable, our Provider must have sufficient funds on Gelato
|
|
||||||
const providerIsLiquid = await gelatoCore.isProviderLiquid(
|
|
||||||
cpk.address,
|
|
||||||
SELF_PROVIDER_GAS_LIMIT, // we need roughtly estimatedGasPerExecution * 3 executions as balance on gelato
|
|
||||||
triggerGasPrice
|
|
||||||
);
|
|
||||||
if (!providerIsLiquid) {
|
|
||||||
console.log(
|
|
||||||
"\n ❌ Ooops! Your GnosisSafe needs to provide more funds to Gelato \n"
|
|
||||||
);
|
|
||||||
console.log("DEMO: run this command: `yarn setup-proxy` first");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For the Demo, make sure the Provider has the Gelato default Executor assigned
|
|
||||||
const assignedExecutor = await gelatoCore.executorByProvider(cpk.address);
|
|
||||||
if (assignedExecutor !== EXECUTOR) {
|
|
||||||
console.log(
|
|
||||||
"\n ❌ Ooops! Your GnosisSafe needs to assign the gelato default Executor \n"
|
|
||||||
);
|
|
||||||
console.log("DEMO: run this command: `yarn setup-proxy` first");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For the Demo, our Provider must use the deployed ProviderModuleGelatoUserProxy
|
|
||||||
const userProxyModuleIsProvided = await gelatoCore.isModuleProvided(
|
|
||||||
cpk.address,
|
|
||||||
PROVIDER_MODULE_GNOSIS
|
|
||||||
);
|
|
||||||
if (!userProxyModuleIsProvided) {
|
|
||||||
console.log(
|
|
||||||
"\n ❌ Ooops! Your GnosisSafe still needs to add ProviderModuleGelatoUserProxy \n"
|
|
||||||
);
|
|
||||||
console.log("DEMO: run this command: `yarn setup-proxy` first");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The transaction to submit a Task to Gelato
|
|
||||||
if (
|
|
||||||
providerIsLiquid &&
|
|
||||||
assignedExecutor === EXECUTOR &&
|
|
||||||
userProxyModuleIsProvided
|
|
||||||
) {
|
|
||||||
// To submit Tasks to Gelato we need to instantiate a GelatoProvider object
|
|
||||||
const myGelatoProvider = new GelatoCoreLib.GelatoProvider({
|
|
||||||
addr: cpk.address, // This time, the provider is paying for the Task, hence we input the Providers address
|
|
||||||
module: PROVIDER_MODULE_GNOSIS,
|
|
||||||
});
|
|
||||||
|
|
||||||
let actionChiMint = await deployments.get("ActionChiMint");
|
|
||||||
actionChiMint = await bre.ethers.getContractAt(
|
|
||||||
actionChiMint.abi,
|
|
||||||
actionChiMint.address
|
|
||||||
);
|
|
||||||
|
|
||||||
// Specify and Instantiate the Gelato Task
|
|
||||||
const taskAutoMintCHIWhenTriggerGasPrice = new GelatoCoreLib.Task({
|
|
||||||
actions: [
|
|
||||||
new GelatoCoreLib.Action({
|
|
||||||
addr: actionChiMint.address,
|
|
||||||
data: await actionChiMint.getActionData(
|
|
||||||
myUserAddress, // recipient of CHI Tokens
|
|
||||||
CHI_TOKENS_TO_MINT // CHI Tokens to be minted
|
|
||||||
),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
termsOkCheck: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
selfProviderGasLimit: SELF_PROVIDER_GAS_LIMIT,
|
|
||||||
// This makes sure we only mint CHI when the gelatoGasPrice is at or below
|
|
||||||
// our desired trigger gas price
|
|
||||||
selfProviderGasPriceCeil: triggerGasPrice,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Specify ExpiryDate: 0 for infinite validity
|
|
||||||
const EXPIRY_DATE = 0;
|
|
||||||
|
|
||||||
// Submit Task to gelato
|
|
||||||
try {
|
|
||||||
const tx = await cpk.execTransactions([
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "submitTask",
|
|
||||||
inputs: [
|
|
||||||
myGelatoProvider,
|
|
||||||
taskAutoMintCHIWhenTriggerGasPrice,
|
|
||||||
EXPIRY_DATE,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
// Wait for mining
|
|
||||||
console.log(`SubmitTask Tx Hash: ${tx.hash}`);
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n PRE taskSubmissionTx error ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,129 +0,0 @@
|
||||||
// We require the Buidler Runtime Environment explicitly here. This is optional
|
|
||||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
|
||||||
// When running the script with `buidler run <script>` you'll find the Buidler
|
|
||||||
// Runtime Environment's members available in the global scope.
|
|
||||||
const bre = require("@nomiclabs/buidler");
|
|
||||||
const ethers = bre.ethers;
|
|
||||||
const { utils } = require("ethers");
|
|
||||||
|
|
||||||
// CPK Library
|
|
||||||
const CPK = require("contract-proxy-kit-custom");
|
|
||||||
|
|
||||||
// running `npx buidler test` automatically makes use of buidler-waffle plugin
|
|
||||||
// => only dependency we need is "chaFi"
|
|
||||||
const { expect } = require("chai");
|
|
||||||
|
|
||||||
const GelatoCoreLib = require("@gelatonetwork/core");
|
|
||||||
|
|
||||||
const GELATO = bre.network.config.deployments.GelatoCore;
|
|
||||||
|
|
||||||
describe("Unproviding ETH deposited on Gelato via GnosisSafe", function () {
|
|
||||||
// No timeout for Mocha due to Rinkeby mining latency
|
|
||||||
this.timeout(0);
|
|
||||||
|
|
||||||
// We use our User Wallet. Per our config this wallet is at the accounts index 0
|
|
||||||
// and hence will be used by default for all transactions we send.
|
|
||||||
let myUserWallet;
|
|
||||||
let myUserAddress;
|
|
||||||
|
|
||||||
// 2) We will deploy a GnosisSafeProxy using the Factory, or if we already deployed
|
|
||||||
// one, we will use that one.
|
|
||||||
let cpk;
|
|
||||||
|
|
||||||
let gelatoCore;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
// We get our User Wallet from the Buidler Runtime Env
|
|
||||||
[myUserWallet] = await bre.ethers.getSigners();
|
|
||||||
myUserAddress = await myUserWallet.getAddress();
|
|
||||||
|
|
||||||
// Create CPK instance connected to new mastercopy
|
|
||||||
cpk = await CPK.create({ ethers, signer: myUserWallet });
|
|
||||||
expect(await cpk.getOwnerAccount()).to.be.equal(myUserAddress);
|
|
||||||
|
|
||||||
const codeAtProxy = await bre.ethers.provider.getCode(cpk.address);
|
|
||||||
const proxyDeployed = codeAtProxy === "0x" ? false : true;
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
\n Network: ${bre.network.name}\
|
|
||||||
\n CPK Proxy address: ${cpk.address}\
|
|
||||||
\n Proxy deployed?: ${proxyDeployed}\n
|
|
||||||
`);
|
|
||||||
|
|
||||||
if (proxyDeployed === false) {
|
|
||||||
console.error("Need `yarn setup-proxy` first");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
gelatoCore = await ethers.getContractAt(
|
|
||||||
GelatoCoreLib.GelatoCore.abi,
|
|
||||||
network.config.deployments.GelatoCore // the Rinkeby Address of the deployed GelatoCore
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Withdraws funds from Gelato and transfer to User", async function () {
|
|
||||||
const fundsOnGelato = await gelatoCore.providerFunds(cpk.address);
|
|
||||||
console.log(
|
|
||||||
`Current funds on Gelato: ${utils.formatEther(fundsOnGelato)} ETH`
|
|
||||||
);
|
|
||||||
|
|
||||||
const prevUserWalletBalance = await myUserWallet.getBalance();
|
|
||||||
console.log(
|
|
||||||
`Current funds in User Wallet: ${utils.formatEther(
|
|
||||||
prevUserWalletBalance
|
|
||||||
)} ETH`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (fundsOnGelato.eq("0")) {
|
|
||||||
console.log(
|
|
||||||
`❌ GnosisSafe ${cpk.address} has no funds on Gelato on ${bre.network.name}`
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`\n Withdrawing ${utils.formatEther(fundsOnGelato)} ETH to userWallet`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const tx = await cpk.execTransactions(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "unprovideFunds",
|
|
||||||
inputs: [fundsOnGelato],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: myUserAddress,
|
|
||||||
value: fundsOnGelato,
|
|
||||||
data: "0x",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{ gasLimit: 2000000 }
|
|
||||||
);
|
|
||||||
// Wait for mining
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
console.log(`Tx Hash: ${tx.hash}`);
|
|
||||||
|
|
||||||
const newFundsOnGelato = await gelatoCore.providerFunds(cpk.address);
|
|
||||||
expect(newFundsOnGelato).to.be.equal(0);
|
|
||||||
console.log(
|
|
||||||
`New funds in Gelato: ${utils.formatEther(newFundsOnGelato)} ETH`
|
|
||||||
);
|
|
||||||
|
|
||||||
const userWalletBalance = await myUserWallet.getBalance();
|
|
||||||
expect(userWalletBalance).to.be.gt(prevUserWalletBalance);
|
|
||||||
console.log(
|
|
||||||
`Funds in UserWallet: ${utils.formatEther(userWalletBalance)} ETH`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n Gelato unprovideFunds error ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,95 +0,0 @@
|
||||||
// We require the Buidler Runtime Environment explicitly here. This is optional
|
|
||||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
|
||||||
// When running the script with `buidler run <script>` you'll find the Buidler
|
|
||||||
// Runtime Environment's members available in the global scope.
|
|
||||||
const bre = require("@nomiclabs/buidler");
|
|
||||||
const ethers = bre.ethers;
|
|
||||||
const { utils } = require("ethers");
|
|
||||||
|
|
||||||
// CPK Library
|
|
||||||
const CPK = require("contract-proxy-kit-custom");
|
|
||||||
|
|
||||||
// running `npx buidler test` automatically makes use of buidler-waffle plugin
|
|
||||||
// => only dependency we need is "chaFi"
|
|
||||||
const { expect } = require("chai");
|
|
||||||
|
|
||||||
describe("Transfering ETH out of GnosisSafe", function () {
|
|
||||||
// No timeout for Mocha due to Rinkeby mining latency
|
|
||||||
this.timeout(0);
|
|
||||||
|
|
||||||
// We use our User Wallet. Per our config this wallet is at the accounts index 0
|
|
||||||
// and hence will be used by default for all transactions we send.
|
|
||||||
let myUserWallet;
|
|
||||||
let myUserAddress;
|
|
||||||
|
|
||||||
// 2) We will deploy a GnosisSafeProxy using the Factory, or if we already deployed
|
|
||||||
// one, we will use that one.
|
|
||||||
let cpk;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
// We get our User Wallet from the Buidler Runtime Env
|
|
||||||
[myUserWallet] = await bre.ethers.getSigners();
|
|
||||||
myUserAddress = await myUserWallet.getAddress();
|
|
||||||
|
|
||||||
// Create CPK instance connected to new mastercopy
|
|
||||||
cpk = await CPK.create({ ethers, signer: myUserWallet });
|
|
||||||
expect(await cpk.getOwnerAccount()).to.be.equal(myUserAddress);
|
|
||||||
|
|
||||||
const codeAtProxy = await bre.ethers.provider.getCode(cpk.address);
|
|
||||||
const proxyDeployed = codeAtProxy === "0x" ? false : true;
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
\n Network: ${bre.network.name}\
|
|
||||||
\n CPK Proxy address: ${cpk.address}\
|
|
||||||
\n Proxy deployed?: ${proxyDeployed}\n
|
|
||||||
`);
|
|
||||||
|
|
||||||
if (proxyDeployed === false) {
|
|
||||||
console.error("Need `yarn setup-proxy` first");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Transfer funds from GnosisSafe", async function () {
|
|
||||||
const prevFundsInUserProxy = await ethers.provider.getBalance(cpk.address);
|
|
||||||
console.log(
|
|
||||||
`Current funds in GnosisSafe: ${utils.formatEther(
|
|
||||||
prevFundsInUserProxy
|
|
||||||
)} ETH`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (prevFundsInUserProxy.eq("0")) {
|
|
||||||
console.log(
|
|
||||||
`❌ GnosisSafe ${cpk.address} has no funds on ${bre.network.name}`
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`\n Transferring ${utils.formatEther(
|
|
||||||
prevFundsInUserProxy
|
|
||||||
)} ETH to ${myUserAddress} on ${bre.network.name}`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const tx = await cpk.execTransactions([
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: myUserAddress,
|
|
||||||
value: prevFundsInUserProxy,
|
|
||||||
data: "0x",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
// Wait for mining
|
|
||||||
console.log(`Tx Hash: ${tx.hash}`);
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
|
|
||||||
const fundsInUserProxy = await ethers.provider.getBalance(cpk.address);
|
|
||||||
expect(fundsInUserProxy).to.be.equal(0);
|
|
||||||
console.log(`New funds in GnosisSafe at ${cpk.address}`);
|
|
||||||
console.log(`${utils.formatEther(fundsInUserProxy)} ETH`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n GnosisSafe transfer funds error ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,200 +0,0 @@
|
||||||
// We require the Buidler Runtime Environment explicitly here. This is optional
|
|
||||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
|
||||||
// When running the script with `buidler run <script>` you'll find the Buidler
|
|
||||||
// Runtime Environment's members available in the global scope.
|
|
||||||
const bre = require("@nomiclabs/buidler");
|
|
||||||
const ethers = bre.ethers;
|
|
||||||
const { constants, utils } = require("ethers");
|
|
||||||
|
|
||||||
// CPK Library
|
|
||||||
const CPK = require("contract-proxy-kit-custom");
|
|
||||||
|
|
||||||
// running `npx buidler test` automatically makes use of buidler-waffle plugin
|
|
||||||
// => only dependency we need is "chaFi"
|
|
||||||
const { expect } = require("chai");
|
|
||||||
|
|
||||||
const GelatoCoreLib = require("@gelatonetwork/core");
|
|
||||||
|
|
||||||
const GELATO = bre.network.config.deployments.GelatoCore;
|
|
||||||
const PROVIDER_MODULE_GNOSIS =
|
|
||||||
bre.network.config.deployments.ProviderModuleGnosisSafeProxy;
|
|
||||||
|
|
||||||
describe("MultiUnprovide on GELATO", function () {
|
|
||||||
// No timeout for Mocha due to Rinkeby mining latency
|
|
||||||
this.timeout(0);
|
|
||||||
|
|
||||||
// We use our User Wallet. Per our config this wallet is at the accounts index 0
|
|
||||||
// and hence will be used by default for all transactions we send.
|
|
||||||
let myUserWallet;
|
|
||||||
let myUserAddress;
|
|
||||||
|
|
||||||
// 2) We will deploy a GnosisSafeProxy using the Factory, or if we already deployed
|
|
||||||
// one, we will use that one.
|
|
||||||
let cpk;
|
|
||||||
|
|
||||||
let gelatoCore;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
// We get our User Wallet from the Buidler Runtime Env
|
|
||||||
[myUserWallet] = await bre.ethers.getSigners();
|
|
||||||
myUserAddress = await myUserWallet.getAddress();
|
|
||||||
gelatoCore = await ethers.getContractAt(
|
|
||||||
GelatoCoreLib.GelatoCore.abi,
|
|
||||||
network.config.deployments.GelatoCore // the Rinkeby Address of the deployed GelatoCore
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get address of CPKFactoryCustom deployment
|
|
||||||
cpkFactoryCustomAddress = (await deployments.get("CPKFactoryCustom"))
|
|
||||||
.address;
|
|
||||||
|
|
||||||
// Create CPK instance connected to new mastercopy
|
|
||||||
cpk = await CPK.create({ ethers, signer: myUserWallet });
|
|
||||||
expect(await cpk.getOwnerAccount()).to.be.equal(myUserAddress);
|
|
||||||
const codeAtProxy = await bre.ethers.provider.getCode(cpk.address);
|
|
||||||
const proxyDeployed = codeAtProxy === "0x" ? false : true;
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
\n Network: ${bre.network.name}\
|
|
||||||
\n CPK Proxy address: ${cpk.address}\
|
|
||||||
\n Proxy deployed?: ${proxyDeployed}\n
|
|
||||||
`);
|
|
||||||
|
|
||||||
if (proxyDeployed === false) {
|
|
||||||
console.error("Need `yarn setup-proxy` first");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Unprovide everything: Executor, Funds, ProviderModule", async function () {
|
|
||||||
const executorIsAssignedToMe =
|
|
||||||
(await gelatoCore.executorByProvider(cpk.address)) ==
|
|
||||||
constants.AddressZero
|
|
||||||
? false
|
|
||||||
: true;
|
|
||||||
|
|
||||||
if (executorIsAssignedToMe) {
|
|
||||||
console.log("\n Sending tx to unassign Executor");
|
|
||||||
// Cleanup TX-1:
|
|
||||||
try {
|
|
||||||
const tx = await cpk.execTransactions([
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "providerAssignsExecutor",
|
|
||||||
inputs: [constants.AddressZero],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
// Wait for mining
|
|
||||||
console.log(`Tx Hash: ${tx.hash}`);
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
expect(await gelatoCore.executorByProvider(cpk.address)).to.be.equal(
|
|
||||||
constants.AddressZero
|
|
||||||
);
|
|
||||||
console.log(`✅ Executor unassigned`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n Cleanup: PRE Executor cleanup TX ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("\n Executor already not assigned ✅ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup TX-3
|
|
||||||
const providedFunds = await gelatoCore.providerFunds(cpk.address);
|
|
||||||
const fundsAreProvided = providedFunds.toString() === "0" ? false : true;
|
|
||||||
if (fundsAreProvided) {
|
|
||||||
console.log(
|
|
||||||
`\n Withdrawing ${utils.formatEther(providedFunds)} ETH to userWallet`
|
|
||||||
);
|
|
||||||
const prevUserWalletBalance = await myUserWallet.getBalance();
|
|
||||||
console.log(
|
|
||||||
`Current funds in User Wallet: ${utils.formatEther(
|
|
||||||
prevUserWalletBalance
|
|
||||||
)} ETH`
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const tx = await cpk.execTransactions(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "unprovideFunds",
|
|
||||||
inputs: [providedFunds],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: myUserAddress,
|
|
||||||
value: providedFunds,
|
|
||||||
data: "0x",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{ gasLimit: 4000000 }
|
|
||||||
);
|
|
||||||
// Wait for mining
|
|
||||||
console.log(`Tx Hash: ${tx.hash}`);
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
|
|
||||||
const newFundsOnGelato = await gelatoCore.providerFunds(cpk.address);
|
|
||||||
expect(newFundsOnGelato).to.be.equal(0);
|
|
||||||
console.log(
|
|
||||||
`✅ New funds in Gelato: ${utils.formatEther(newFundsOnGelato)} ETH`
|
|
||||||
);
|
|
||||||
|
|
||||||
const userWalletBalance = await myUserWallet.getBalance();
|
|
||||||
expect(userWalletBalance).to.be.gt(prevUserWalletBalance);
|
|
||||||
console.log(
|
|
||||||
`✅ Funds in UserWallet: ${utils.formatEther(userWalletBalance)} ETH`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n Gelato unprovideFunds error ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("\n Already no Funds on Gelato ✅ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup TX-3
|
|
||||||
const moduleIsProvided = await gelatoCore.isModuleProvided(
|
|
||||||
cpk.address,
|
|
||||||
PROVIDER_MODULE_GNOSIS
|
|
||||||
);
|
|
||||||
if (moduleIsProvided) {
|
|
||||||
console.log(`\n Removing ProviderModuleGnosisSafeProxy`);
|
|
||||||
try {
|
|
||||||
const tx = await cpk.execTransactions([
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "removeProviderModules",
|
|
||||||
inputs: [[PROVIDER_MODULE_GNOSIS]],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
// Wait for mining
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
console.log(`Tx Hash: ${tx.hash}`);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await gelatoCore.isModuleProvided(cpk.address, PROVIDER_MODULE_GNOSIS)
|
|
||||||
).to.be.false;
|
|
||||||
console.log(`✅ ProviderModuleGnosisSafeProxy removed`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n Gelato removeProviderModules error ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("\n Already removed ProviderModule from Gelato ✅ ");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,186 +0,0 @@
|
||||||
// We require the Buidler Runtime Environment explicitly here. This is optional
|
|
||||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
|
||||||
// When running the script with `buidler run <script>` you'll find the Buidler
|
|
||||||
// Runtime Environment's members available in the global scope.
|
|
||||||
const bre = require("@nomiclabs/buidler");
|
|
||||||
const ethers = bre.ethers;
|
|
||||||
const { utils } = require("ethers");
|
|
||||||
const fetch = require("node-fetch");
|
|
||||||
|
|
||||||
// CPK Library
|
|
||||||
const CPK = require("contract-proxy-kit");
|
|
||||||
|
|
||||||
// running `npx buidler test` automatically makes use of buidler-waffle plugin
|
|
||||||
// => only dependency we need is "chaFi"
|
|
||||||
const { expect } = require("chai");
|
|
||||||
|
|
||||||
const GelatoCoreLib = require("@gelatonetwork/core");
|
|
||||||
|
|
||||||
const GELATO = bre.network.config.deployments.GelatoCore;
|
|
||||||
|
|
||||||
// Current Gelato Gas Price
|
|
||||||
let currentGelatoGasPrice;
|
|
||||||
|
|
||||||
// TRIGGER GAS PRICE
|
|
||||||
let triggerGasPrice;
|
|
||||||
|
|
||||||
// The Graph query
|
|
||||||
const taskWrapperQuery = (proxyAddress) => {
|
|
||||||
return `
|
|
||||||
{
|
|
||||||
taskReceiptWrappers(where: {user: "${proxyAddress}"}) {
|
|
||||||
taskReceipt {
|
|
||||||
id
|
|
||||||
userProxy
|
|
||||||
provider {
|
|
||||||
addr
|
|
||||||
module
|
|
||||||
}
|
|
||||||
index
|
|
||||||
tasks {
|
|
||||||
conditions {
|
|
||||||
inst
|
|
||||||
data
|
|
||||||
}
|
|
||||||
actions {
|
|
||||||
addr
|
|
||||||
data
|
|
||||||
operation
|
|
||||||
dataFlow
|
|
||||||
value
|
|
||||||
termsOkCheck
|
|
||||||
}
|
|
||||||
selfProviderGasLimit
|
|
||||||
selfProviderGasPriceCeil
|
|
||||||
}
|
|
||||||
expiryDate
|
|
||||||
cycleId
|
|
||||||
submissionsLeft
|
|
||||||
}
|
|
||||||
submissionHash
|
|
||||||
status
|
|
||||||
submissionDate
|
|
||||||
executionDate
|
|
||||||
executionHash
|
|
||||||
selfProvided
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("Canceling ActionCHIMint Task on Gelato via GnosisSafe", function () {
|
|
||||||
// No timeout for Mocha due to Rinkeby mining latency
|
|
||||||
this.timeout(0);
|
|
||||||
|
|
||||||
// We use our User Wallet. Per our config this wallet is at the accounts index 0
|
|
||||||
// and hence will be used by default for all transactions we send.
|
|
||||||
let myUserWallet;
|
|
||||||
let myUserAddress;
|
|
||||||
|
|
||||||
// 2) We will deploy a GnosisSafeProxy using the Factory, or if we already deployed
|
|
||||||
// one, we will use that one.
|
|
||||||
let cpk;
|
|
||||||
|
|
||||||
let gelatoCore;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
// We get our User Wallet from the Buidler Runtime Env
|
|
||||||
[myUserWallet] = await bre.ethers.getSigners();
|
|
||||||
myUserAddress = await myUserWallet.getAddress();
|
|
||||||
|
|
||||||
// Create CPK instance connected to new mastercopy
|
|
||||||
cpk = await CPK.create({ ethers, signer: myUserWallet });
|
|
||||||
expect(await cpk.getOwnerAccount()).to.be.equal(myUserAddress);
|
|
||||||
|
|
||||||
const codeAtProxy = bre.ethers.provider.getCode(cpk.address);
|
|
||||||
const proxyDeployed = codeAtProxy === "0x" ? false : true;
|
|
||||||
|
|
||||||
console.log(`
|
|
||||||
\n Network: ${bre.network.name}\
|
|
||||||
\n CPK Proxy address: ${cpk.address}\
|
|
||||||
\n Proxy deployed?: ${proxyDeployed}\n
|
|
||||||
`);
|
|
||||||
|
|
||||||
gelatoCore = await ethers.getContractAt(
|
|
||||||
GelatoCoreLib.GelatoCore.abi,
|
|
||||||
network.config.deployments.GelatoCore // the Rinkeby Address of the deployed GelatoCore
|
|
||||||
);
|
|
||||||
|
|
||||||
currentGelatoGasPrice = await bre.run("fetchGelatoGasPrice");
|
|
||||||
|
|
||||||
// FOR TESTING WE SET IT EQUAL TO CURRENT SO WE CAN CHECK FOR EXECUTION
|
|
||||||
triggerGasPrice = currentGelatoGasPrice;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Submit your Task to Gelato via your GelatoUserProxy
|
|
||||||
it("User cancels Task as SelfProvider", async function () {
|
|
||||||
// 1. Fetch all taskReceipts that the UserProxy has submitted
|
|
||||||
const response = await fetch(
|
|
||||||
`https://api.thegraph.com/subgraphs/name/gelatodigital/gelato-network-rinkeby`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({
|
|
||||||
query: taskWrapperQuery(cpk.address.toLowerCase()),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// 2. Convert Response to Json and get taskReceiptWrappers
|
|
||||||
const json = await response.json();
|
|
||||||
const taskReceiptWrappers = json.data.taskReceiptWrappers;
|
|
||||||
console.log(taskReceiptWrappers);
|
|
||||||
|
|
||||||
// 3. Select only the CHi Tasks
|
|
||||||
let actionChiMint = await deployments.get("ActionChiMint");
|
|
||||||
const chiActionWrappers = taskReceiptWrappers.filter(
|
|
||||||
(wrapper) =>
|
|
||||||
utils.getAddress(wrapper.taskReceipt.tasks[0].actions[0].addr) ===
|
|
||||||
utils.getAddress(actionChiMint.address)
|
|
||||||
);
|
|
||||||
// console.log(chiActionWrappers);
|
|
||||||
|
|
||||||
// 4. Get first Chi Task where status == 'awaitingExec'
|
|
||||||
const taskReceiptWrapper = chiActionWrappers.find(
|
|
||||||
(wrapper) => wrapper.status === "awaitingExec"
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(taskReceiptWrapper);
|
|
||||||
|
|
||||||
if (taskReceiptWrapper) {
|
|
||||||
try {
|
|
||||||
// 5. Decode Task Receipt
|
|
||||||
const chiActionInputs = ethers.utils.defaultAbiCoder.decode(
|
|
||||||
["address", "uint256"],
|
|
||||||
ethers.utils.hexDataSlice(
|
|
||||||
taskReceiptWrapper.taskReceipt.tasks[0].actions[0].data,
|
|
||||||
4
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`Recipient: ${chiActionInputs[0]}`);
|
|
||||||
console.log(`Chi Amount: ${chiActionInputs[1]}`);
|
|
||||||
|
|
||||||
// 6. Cancel Task on gelato if there is a pending task to cancel
|
|
||||||
const tx = await cpk.execTransactions([
|
|
||||||
{
|
|
||||||
operation: CPK.CALL,
|
|
||||||
to: GELATO,
|
|
||||||
value: 0,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: GelatoCoreLib.GelatoCore.abi,
|
|
||||||
functionname: "cancelTask",
|
|
||||||
inputs: [taskReceiptWrapper.taskReceipt],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
// Wait for mining
|
|
||||||
console.log(`Cancel Task Receipt Tx Hash: ${tx.hash}`);
|
|
||||||
await tx.transactionResponse.wait();
|
|
||||||
console.log(`Cancel Task Receipt Success!`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("\n PRE Cancel Task Receipt error ❌ \n", error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -9,7 +9,9 @@ const { sleep } = GelatoCoreLib;
|
||||||
// Constants
|
// Constants
|
||||||
const INSTA_MASTER = "0xfCD22438AD6eD564a1C26151Df73F6B33B817B56";
|
const INSTA_MASTER = "0xfCD22438AD6eD564a1C26151Df73F6B33B817B56";
|
||||||
const DAI_100 = ethers.utils.parseUnits("100", 18);
|
const DAI_100 = ethers.utils.parseUnits("100", 18);
|
||||||
const APY_2_PERCENT_IN_SECONDS = 1000000000627937192491029810;
|
const APY_2_PERCENT_IN_SECONDS = ethers.BigNumber.from(
|
||||||
|
"1000000000627937192491029810"
|
||||||
|
);
|
||||||
|
|
||||||
// Contracts
|
// Contracts
|
||||||
const InstaIndex = require("../pre-compiles/InstaIndex.json");
|
const InstaIndex = require("../pre-compiles/InstaIndex.json");
|
||||||
|
@ -45,7 +47,6 @@ describe("Move DAI lending from DSR to Compound", function () {
|
||||||
let mockDSR;
|
let mockDSR;
|
||||||
let mockCDAI;
|
let mockCDAI;
|
||||||
let conditionCompareUints;
|
let conditionCompareUints;
|
||||||
let conditionHasBalanceAndAllowance;
|
|
||||||
let connectGelato;
|
let connectGelato;
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
|
@ -55,8 +56,8 @@ describe("Move DAI lending from DSR to Compound", function () {
|
||||||
const instaMaster = await ethers.provider.getSigner(INSTA_MASTER);
|
const instaMaster = await ethers.provider.getSigner(INSTA_MASTER);
|
||||||
|
|
||||||
// Ganache default accounts prefilled with 100 ETH
|
// Ganache default accounts prefilled with 100 ETH
|
||||||
expect(await userWallet.getBalance()).to.be.equal(
|
expect(await userWallet.getBalance()).to.be.gt(
|
||||||
ethers.utils.parseEther("100")
|
ethers.utils.parseEther("10")
|
||||||
);
|
);
|
||||||
|
|
||||||
// ===== DSA SETUP ==================
|
// ===== DSA SETUP ==================
|
||||||
|
@ -159,12 +160,6 @@ describe("Move DAI lending from DSR to Compound", function () {
|
||||||
conditionCompareUints = await ConditionCompareUintsFromTwoSources.deploy();
|
conditionCompareUints = await ConditionCompareUintsFromTwoSources.deploy();
|
||||||
await conditionCompareUints.deployed();
|
await conditionCompareUints.deployed();
|
||||||
|
|
||||||
const ConditionHasBalanceAndAllowance = await ethers.getContractFactory(
|
|
||||||
"ConditionHasBalanceAndAllowance"
|
|
||||||
);
|
|
||||||
conditionHasBalanceAndAllowance = await ConditionHasBalanceAndAllowance.deploy();
|
|
||||||
await conditionHasBalanceAndAllowance.deployed();
|
|
||||||
|
|
||||||
// ===== Dapp Dependencies SETUP ==================
|
// ===== Dapp Dependencies SETUP ==================
|
||||||
// This test assumes our user has 100 DAI deposited in Maker DSR
|
// This test assumes our user has 100 DAI deposited in Maker DSR
|
||||||
dai = await ethers.getContractAt(IERC20.abi, bre.network.config.DAI);
|
dai = await ethers.getContractAt(IERC20.abi, bre.network.config.DAI);
|
||||||
|
@ -212,16 +207,16 @@ describe("Move DAI lending from DSR to Compound", function () {
|
||||||
const rebalanceCondition = new GelatoCoreLib.Condition({
|
const rebalanceCondition = new GelatoCoreLib.Condition({
|
||||||
inst: conditionCompareUints.address,
|
inst: conditionCompareUints.address,
|
||||||
data: await conditionCompareUints.getConditionData(
|
data: await conditionCompareUints.getConditionData(
|
||||||
mockDSR.address,
|
mockCDAI.address, // We are in DSR so we compare against CDAI => SourceA=CDAI
|
||||||
mockCDAI.address,
|
mockDSR.address, // SourceB=DSR
|
||||||
await bre.run("abi-encode-withselector", {
|
|
||||||
abi: require("../artifacts/MockDSR.json").abi,
|
|
||||||
functionname: "dsr",
|
|
||||||
}),
|
|
||||||
await bre.run("abi-encode-withselector", {
|
await bre.run("abi-encode-withselector", {
|
||||||
abi: require("../artifacts/MockCDAI.json").abi,
|
abi: require("../artifacts/MockCDAI.json").abi,
|
||||||
functionname: "supplyRatePerSecond",
|
functionname: "supplyRatePerSecond",
|
||||||
}),
|
}), // CDAI data feed first (sourceAData)
|
||||||
|
await bre.run("abi-encode-withselector", {
|
||||||
|
abi: require("../artifacts/MockDSR.json").abi,
|
||||||
|
functionname: "dsr",
|
||||||
|
}), // DSR data feed second (sourceBData)
|
||||||
MIN_SPREAD
|
MIN_SPREAD
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@ -401,7 +396,13 @@ describe("Move DAI lending from DSR to Compound", function () {
|
||||||
const dsaCDAIBefore = await cDAI.balanceOf(dsa.address);
|
const dsaCDAIBefore = await cDAI.balanceOf(dsa.address);
|
||||||
|
|
||||||
// For testing we now simulate automatic Task Execution ❗
|
// For testing we now simulate automatic Task Execution ❗
|
||||||
await expect(gelatoCore.exec(taskReceipt)).to.emit("LogExecSuccess");
|
const gelatoGasPrice = await bre.run("fetchGelatoGasPrice");
|
||||||
|
await expect(
|
||||||
|
gelatoCore.exec(taskReceipt, {
|
||||||
|
gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei)
|
||||||
|
gasLimit: taskRebalanceDSRToCDAIifBetter.selfProviderGasLimit,
|
||||||
|
})
|
||||||
|
).to.emit(gelatoCore, "LogExecSuccess");
|
||||||
|
|
||||||
// Since the Execution was successful, we now expect our DSA to hold more
|
// Since the Execution was successful, we now expect our DSA to hold more
|
||||||
// CDAI then before. This concludes our testing.
|
// CDAI then before. This concludes our testing.
|
Loading…
Reference in New Issue
Block a user