automatic DSR - CDAI rebalance prototype tested

This commit is contained in:
gitpusha 2020-08-24 16:21:47 +02:00
parent 7ce48fd5ea
commit 3956eab93f
13 changed files with 5528 additions and 1479 deletions

30
.circleci/config.yml Normal file
View 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

View File

@ -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

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 398 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -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);
}
});
});

View File

@ -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");
}
});
});

View File

@ -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);
}
}
});
});

View File

@ -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);
}
});
});

View File

@ -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);
}
});
});

View File

@ -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 ✅ ");
}
});
});

View File

@ -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);
}
}
});
});

View File

@ -9,7 +9,9 @@ const { sleep } = GelatoCoreLib;
// Constants
const INSTA_MASTER = "0xfCD22438AD6eD564a1C26151Df73F6B33B817B56";
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
const InstaIndex = require("../pre-compiles/InstaIndex.json");
@ -45,7 +47,6 @@ describe("Move DAI lending from DSR to Compound", function () {
let mockDSR;
let mockCDAI;
let conditionCompareUints;
let conditionHasBalanceAndAllowance;
let connectGelato;
before(async function () {
@ -55,8 +56,8 @@ describe("Move DAI lending from DSR to Compound", function () {
const instaMaster = await ethers.provider.getSigner(INSTA_MASTER);
// Ganache default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.equal(
ethers.utils.parseEther("100")
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
// ===== DSA SETUP ==================
@ -159,12 +160,6 @@ describe("Move DAI lending from DSR to Compound", function () {
conditionCompareUints = await ConditionCompareUintsFromTwoSources.deploy();
await conditionCompareUints.deployed();
const ConditionHasBalanceAndAllowance = await ethers.getContractFactory(
"ConditionHasBalanceAndAllowance"
);
conditionHasBalanceAndAllowance = await ConditionHasBalanceAndAllowance.deploy();
await conditionHasBalanceAndAllowance.deployed();
// ===== Dapp Dependencies SETUP ==================
// This test assumes our user has 100 DAI deposited in Maker DSR
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({
inst: conditionCompareUints.address,
data: await conditionCompareUints.getConditionData(
mockDSR.address,
mockCDAI.address,
await bre.run("abi-encode-withselector", {
abi: require("../artifacts/MockDSR.json").abi,
functionname: "dsr",
}),
mockCDAI.address, // We are in DSR so we compare against CDAI => SourceA=CDAI
mockDSR.address, // SourceB=DSR
await bre.run("abi-encode-withselector", {
abi: require("../artifacts/MockCDAI.json").abi,
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
),
});
@ -401,7 +396,13 @@ describe("Move DAI lending from DSR to Compound", function () {
const dsaCDAIBefore = await cDAI.balanceOf(dsa.address);
// 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
// CDAI then before. This concludes our testing.