Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/cmd #5

Merged
merged 6 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: "publish package to npm"

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: node
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://registry.npmjs.org
- name: publish
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
58 changes: 22 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,44 @@ This is a script for automatic interaction with the onchain protocol by setting
# Supported protocols

1. Base AAVE
- deposit 0.001 ETH -> approve -> withdraw
- Deposit ETH -> approve -> withdraw
2. Base Uniswap
- swap DAI <-> 0.0001 ETH
- Swap DAI <-> ETH

# How to start ?
# Getting started

Two options for starting the bot

1. Node.js
2. Docker

## Prerequisites

Create `.env` file with `.env.example`

You can change default value of variables in `.env.example` to `.env` if you need

## Docker

1. Build image
1. Install CLI globally

```bash
npm run docker:build
npm install -g airdrop-bot
```

2. Run container
2. Check version

```bash
npm run docker:run
airdrop-bot -v
# 0.0.1
```

3. Check docker logs
3. Interact protocols (AAVE / Uniswap)

```bash
npm run docker:logs
```

## Node.js

1. Install packages
For example, interact aave by 0.01 ETH and setup delay 10 minutes

```bash
yarn
```
# set your private key to environment variables
export PK=<your-private-key>

2. Run app
# check private key
echo $PK
```

```bash
npm run app
# execute AAVE
airdrop-bot aave -c "0 */10 * * * *" -p $PK --delay 10 --amount 0.01
```

Notice: cron job expression is from **second** not minute

---

If successfully started, the following information should appear.
Expand All @@ -65,15 +52,14 @@ If successfully started, the following information should appear.
> ts-node ./app.ts

Starting app ...
Mode <$MODE>
Time <$CRONJOB> | Delay <$DELAY> minutes
Time 0 */10 * * * * | Delay 10 minutes
Account <Public address from $PK>
```

Print below information when starting a new round

- Trigger start/end time (yime zone: Asia/Taipei)
- Random delay seconds (limit base on `$DELAY` variable in `.env`)
- Trigger start / end time (time zone: Asia/Taipei)
- Random delay seconds (limit base on `--delay` command option)
- Each txs hash and status (retry sent tx if status is `reverted`)

```bash
Expand Down
19 changes: 19 additions & 0 deletions dist/commands/aave.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.aave = void 0;
const viem_1 = require("viem");
const commander_1 = require("commander");
const protocol_1 = require("../types/protocol");
const aave_1 = require("../utils/aave");
const cron_1 = require("../libs/cron");
exports.aave = new commander_1.Command("aave");
exports.aave
.description("Start the aave protocol")
.option("-p, --pk <private key>", "private key of signer")
.option(" --amount <amount>", "amount of ETH")
.option("-d, --delay <delay time>", "delay in minutes")
.option("-c, --cronjob <cron job...>", 'cron job expression i.e. "*/5 * * * *"')
.action((options) => {
const aaveProtocol = new protocol_1.Protocol(aave_1.aave, options.pk, (0, viem_1.parseEther)(options.amount), (0, cron_1.parseCronJob)(options.cronjob), Number(options.delay));
aaveProtocol.execute();
});
19 changes: 19 additions & 0 deletions dist/commands/uniswap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.uniswap = void 0;
const viem_1 = require("viem");
const commander_1 = require("commander");
const protocol_1 = require("../types/protocol");
const uniswap_1 = require("../utils/uniswap");
const cron_1 = require("../libs/cron");
exports.uniswap = new commander_1.Command("uniswap");
exports.uniswap
.description("Start the uniswap protocol")
.option("-p, --pk <private key>", "private key of signer")
.option(" --amount <amount>", "amount of ETH")
.option("-d, --delay <delay time>", "delay in minutes")
.option("-c, --cronjob <cron job...>", 'cron job expression (every five seconds i.e. "*/5 * * * *")')
.action((options) => {
const uniswapProtocol = new protocol_1.Protocol(uniswap_1.uniswap, options.pk, (0, viem_1.parseEther)(options.amount), (0, cron_1.parseCronJob)(options.cronjob), Number(options.delay));
uniswapProtocol.execute();
});
18 changes: 18 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.program = void 0;
const commander_1 = require("commander");
const figlet_1 = require("figlet");
const aave_1 = require("./commands/aave");
const uniswap_1 = require("./commands/uniswap");
console.log((0, figlet_1.textSync)("BOT"));
exports.program = new commander_1.Command();
// Main
exports.program
.version("0.0.1", "-v, --versions", "output the current version")
.description("A simple airdrop bot")
.addCommand(uniswap_1.uniswap)
.addCommand(aave_1.aave)
.showHelpAfterError("(add --help for additional information)");
exports.program.parse();
40 changes: 40 additions & 0 deletions dist/libs/cron.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseCronJob = exports.createConfig = void 0;
const time_1 = require("./time");
function createConfig(interaction, cronTime, delay) {
return {
cronTime: cronTime,
onTick: function () {
return __awaiter(this, void 0, void 0, function* () {
console.log("Start new round");
yield (0, time_1.randomDelay)(delay);
yield interaction();
console.log(`End time ${(0, time_1.getTime)()}`);
console.log("=============================");
});
},
start: true,
timeZone: "Asia/Taipei",
};
}
exports.createConfig = createConfig;
function parseCronJob(cronJob) {
const parseResult = cronJob.map((job) => {
if (job.includes('"')) {
return job.replace('"', "");
}
return job;
});
return parseResult.join(" ");
}
exports.parseCronJob = parseCronJob;
39 changes: 39 additions & 0 deletions dist/libs/time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.delay = exports.randomDelay = exports.getTime = void 0;
function getTime() {
const date = new Date(Date.now());
const formatStakeTime = date.toLocaleString("zh-TW", {
timeZone: "Asia/Taipei",
});
return formatStakeTime;
}
exports.getTime = getTime;
function randomDelay(limit) {
return __awaiter(this, void 0, void 0, function* () {
const MINUTES = 60; // 60 seconds
const LIMIT = Number(limit) * MINUTES; // 最多不超過延遲 $DELAY 分鐘
const rand = Math.floor(Math.random() * LIMIT);
console.log(`Start time: ${getTime()} | Delay ${rand} seconds`);
yield delay(rand * 1000);
});
}
exports.randomDelay = randomDelay;
function delay(ms) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
});
}
exports.delay = delay;
// -c "*/5 * * * * *" -d 0 -p $PK --amount 0.01
30 changes: 30 additions & 0 deletions dist/libs/transaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendTransaction = void 0;
const public_1 = require("../utils/clients/public");
function sendTransaction(request, signer) {
return __awaiter(this, void 0, void 0, function* () {
while (1) {
const hash = yield signer.writeContract(request);
const transaction = yield public_1.PUBLIC_CLIENT.waitForTransactionReceipt({
confirmations: 5,
hash,
pollingInterval: 12000,
});
console.log(`Tx Hash: ${transaction.transactionHash} - ${transaction.status}`);
if (transaction.status === "success")
break;
console.log("Reverted !! Retrying...");
}
});
}
exports.sendTransaction = sendTransaction;
36 changes: 36 additions & 0 deletions dist/types/protocol.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Protocol = void 0;
const cron_1 = require("cron");
const viem_1 = require("viem");
const accounts_1 = require("viem/accounts");
const cron_2 = require("../libs/cron");
const config_1 = require("../utils/clients/config");
class Protocol {
constructor(interaction, privateKey, amount, cronTime = "* * * * * *", delay = 0) {
this.amount = amount;
this.cronTime = cronTime;
this.delay = delay;
const _signer = this.setSigner(privateKey);
const _interaction = interaction.bind(null, _signer, amount);
const config = (0, cron_2.createConfig)(_interaction, cronTime, delay);
this.cron = new cron_1.CronJob(config.cronTime, config.onTick, null, config.start, config.timeZone);
}
execute() {
var _a;
console.log(`Starting app ...`);
console.log(`Time ${this.cronTime} | Delay ${this.delay} minutes`);
console.log(`Account ${(_a = this.signer.account) === null || _a === void 0 ? void 0 : _a.address}`);
this.cron.start();
}
getSigner() {
return this.signer;
}
setSigner(privateKey) {
const account = (0, accounts_1.privateKeyToAccount)(privateKey);
const _signer = (0, viem_1.createWalletClient)(Object.assign({ account }, config_1.CONFIG));
this.signer = _signer;
return _signer;
}
}
exports.Protocol = Protocol;
42 changes: 42 additions & 0 deletions dist/utils/aave.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.aave = void 0;
const viem_1 = require("viem");
const transaction_1 = require("../libs/transaction");
const aave_1 = require("./contracts/aave");
function deposit(signer, amount) {
return __awaiter(this, void 0, void 0, function* () {
const { account } = signer;
const { request } = yield aave_1.AAVE.simulate.depositETH([viem_1.zeroAddress, account.address, 0], {
value: amount,
account,
});
console.log("Deposit...");
yield (0, transaction_1.sendTransaction)(request, signer);
});
}
function withdraw(signer, amount) {
return __awaiter(this, void 0, void 0, function* () {
const { account } = signer;
const { request: approveReq } = yield aave_1.WETH.simulate.approve([aave_1.AAVE.address, amount], { account: account });
console.log("Approving...");
yield (0, transaction_1.sendTransaction)(approveReq, signer);
const { request: withdrawReq } = yield aave_1.AAVE.simulate.withdrawETH([viem_1.zeroAddress, amount, account.address], { account: account });
console.log("Withdraw...");
yield (0, transaction_1.sendTransaction)(withdrawReq, signer);
});
}
const aave = (_signer, amount) => __awaiter(void 0, void 0, void 0, function* () {
yield deposit(_signer, amount);
yield withdraw(_signer, amount);
});
exports.aave = aave;
Loading
Loading