Skip to content

Commit

Permalink
Airdrop api
Browse files Browse the repository at this point in the history
  • Loading branch information
fewensa committed Jan 18, 2022
1 parent 23ef785 commit dce795e
Show file tree
Hide file tree
Showing 12 changed files with 447 additions and 0 deletions.
46 changes: 46 additions & 0 deletions .github/workflows/deploy-prd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,55 @@ jobs:
- name: Build
run: yarn build

- name: Generate config data
run: |
jq -n \
--arg crab_smart_seed "${{ secrets.CRAB_SMART_SEED }}" \
'{
crab_smart_seed: $crab_smart_seed
}' | tee chain_data.json
jq -n \
--arg github_oauth_app_key "${{ secrets.X_GITHUB_OAUTH_APP_KEY }}" \
--arg github_oauth_app_secret "${{ secrets.X_GITHUB_OAUTH_APP_SECRETS }}" \
'{
github_oauth_app_key: $github_oauth_app_key,
github_oauth_app_secret: $github_oauth_app_secret
}' | tee grant_data.json
jq -n \
--arg redis_host "${{ secrets.REDIS_HOST }}" \
--arg redis_port "${{ secrets.REDIS_PORT }}" \
--arg redis_password "${{ secrets.REDIS_PASSWORD }}" \
'{
redis_host: $redis_host,
redis_port: $redis_port,
redis_password: $redis_password
}' | tee redis_data.json
- name: Render chain config
uses: jayamanikharyono/jinja-action@v0.1
with:
datafile: chain_data.json
path: airdrop/api/config/chain.json

- name: Render grant config
uses: jayamanikharyono/jinja-action@v0.1
with:
datafile: grant_data.json
path: airdrop/api/config/grant.json

- name: Render redis config
uses: jayamanikharyono/jinja-action@v0.1
with:
datafile: redis_data.json
path: airdrop/api/config/redis.json

- name: Prepare Project Config
run: |
mv airdrop/api/config/chain.json airdrop/api/config/chain.safe.json
mv airdrop/api/config/grant.json airdrop/api/config/grant.safe.json
mv airdrop/api/config/redis.json airdrop/api/config/redis.safe.json
mv vercel.json build/
mv airdrop/* build/
- name: Deploy
id: deploy
Expand Down
47 changes: 47 additions & 0 deletions .github/workflows/deploy-stg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: Deploy staging

on:
push:
branches:

jobs:
deploy-develop:
Expand Down Expand Up @@ -32,9 +33,55 @@ jobs:
- name: Build
run: yarn build

- name: Generate config data
run: |
jq -n \
--arg crab_smart_seed "${{ secrets.CRAB_SMART_SEED }}" \
'{
crab_smart_seed: $crab_smart_seed
}' | tee chain_data.json
jq -n \
--arg github_oauth_app_key "${{ secrets.X_GITHUB_OAUTH_APP_KEY }}" \
--arg github_oauth_app_secret "${{ secrets.X_GITHUB_OAUTH_APP_SECRETS }}" \
'{
github_oauth_app_key: $github_oauth_app_key,
github_oauth_app_secret: $github_oauth_app_secret
}' | tee grant_data.json
jq -n \
--arg redis_host "${{ secrets.REDIS_HOST }}" \
--arg redis_port "${{ secrets.REDIS_PORT }}" \
--arg redis_password "${{ secrets.REDIS_PASSWORD }}" \
'{
redis_host: $redis_host,
redis_port: $redis_port,
redis_password: $redis_password
}' | tee redis_data.json
- name: Render chain config
uses: jayamanikharyono/jinja-action@v0.1
with:
datafile: chain_data.json
path: airdrop/api/config/chain.json

- name: Render grant config
uses: jayamanikharyono/jinja-action@v0.1
with:
datafile: grant_data.json
path: airdrop/api/config/grant.json

- name: Render redis config
uses: jayamanikharyono/jinja-action@v0.1
with:
datafile: redis_data.json
path: airdrop/api/config/redis.json

- name: Prepare Project Config
run: |
mv airdrop/api/config/chain.json airdrop/api/config/chain.safe.json
mv airdrop/api/config/grant.json airdrop/api/config/grant.safe.json
mv airdrop/api/config/redis.json airdrop/api/config/redis.safe.json
mv vercel.json build/
mv airdrop/* build/
- name: Deploy
id: deploy
Expand Down
14 changes: 14 additions & 0 deletions airdrop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
**/*.DS_Store
target
.idea

/Cargo.lock

scripts/*.local.sh

package-lock.json
node_modules
*.safe.json


.vercel
219 changes: 219 additions & 0 deletions airdrop/api/airdrop/transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import type {VercelRequest, VercelResponse} from '@vercel/node';
import {Octokit} from '@octokit/rest';
import * as qs from 'qs';
import is from 'is_js';
import Web3 from 'web3';

const Redis = require('ioredis');

const AMOUNT = 10;



// request
export default async function (req: VercelRequest, res: VercelResponse) {
const data = qs.parse(req.body);

res.setHeader('content-type', 'application/json');

// check data
if (!data) {
res.statusCode = 400;
const body = {
err: 1,
message: 'Not have data'
};
res.end(JSON.stringify(body, null, 2));
return;
}

// check address
if (is.not.truthy(data.address)) {
res.statusCode = 400;
const body = {
err: 1,
message: 'No address found, please type receiver address'
};
res.end(JSON.stringify(body, null, 2));
return;
}

// query github account info
let user;
try {
const cookies = req.cookies;
const accessToken = cookies['x-access-token'];
const octokit = new Octokit({
auth: accessToken,
});
const {data} = await octokit.request("/user");
user = data;
} catch (e) {
res.statusCode = 401;
const body = {
err: 1,
message: 'Authorization failed. please try login again'
};
res.end(JSON.stringify(body, null, 2));
return;
}

// check account registry time
const created_at = new Date(user.created_at);
const end = new Date('2021-12-30T00:00:00Z');
if (+created_at > +end) {
res.statusCode = 400;
const body = {
err: 1,
message: 'Your account does not meet the rules'
};
res.end(JSON.stringify(body, null, 2));
return;
}



const client = redis();
// const chainName = data.chain.toUpperCase().trim();
const chainName = 'CRAB';
const cacheKey = `${chainName}-${user.id}`;

const record = await client.get(cacheKey);

// check already sent
if (record != null) {
res.statusCode = 400;
const body = {
err: 1,
message: 'You have already received',
data: {
state: 'RECEIVED',
time: record,
}
};
res.end(JSON.stringify(body, null, 2));
return;
}

try {
// transfer
const result = await transfer(chainName, data.address);
if (result == null) {
res.statusCode = 400;
const body = {
err: 1,
message: 'Transfer failed. please connect team',
};
res.end(JSON.stringify(body, null, 2));
return;
}
if (result instanceof String || (typeof result == 'string')) {
res.statusCode = 400;
const body = {
err: 1,
message: result,
};
res.end(JSON.stringify(body, null, 2));
return;
}

// put sent time for user
await client.set(cacheKey, +new Date());

res.statusCode = 200;
const body = {
err: 0,
data: result,
};
res.end(JSON.stringify(body, null, 2));

} catch (e) {
res.statusCode = 400;
const body = {
err: 1,
message: 'Transfer failed: ' + e.message,
};
res.end(JSON.stringify(body, null, 2));
}
}


async function transfer(chain: String, address: String): Promise<TransferReceipt | String | null> {
const chainName = chain.toUpperCase();
console.log(`Transfer chain ${chainName} to ${address}`);
switch (chainName) {
case 'CRAB':
return await _transferCrab(address);
default:
return `Not support this chain: ${chainName}`;
}
}


async function _transferCrab(address: String): Promise<TransferReceipt | String> {
const web3 = crabSmartApi();
const chain = require('../config/chain.safe.json').crab_smart;

let receipt;
try {
const balanceFrom = web3.utils.fromWei(
await web3.eth.getBalance(chain.address),
'ether'
);
// const balanceTo = web3.utils.fromWei(
// await web3.eth.getBalance(address.toString()),
// 'ether'
// );
if (balanceFrom == null) {
return 'Not have more balance to transfer';
}
if (+balanceFrom <= AMOUNT) {
return 'All airdrops have ended';
}

const tx = await web3.eth.accounts.signTransaction({
from: chain.address,
to: address.toString(),
value: web3.utils.toWei(AMOUNT.toString(), 'ether'),
gas: 40000,
}, chain.seed);

receipt = await web3.eth.sendSignedTransaction(tx.rawTransaction);
} catch (err) {
console.error(err);
return 'Failed to sign transactions: ' + err.message;
}
const hash = receipt.transactionHash;

return {tx: hash, preview: `https://crab.subview.xyz/tx/${hash}`,}
}





let _redis;

function redis() {
if (_redis) return _redis;
const config = require('../config/redis.safe.json');
_redis = new Redis(`rediss://:${config.password}@${config.host}:${config.port}`);
return _redis;
}


let _crabSmartApi;

function crabSmartApi(): Web3 {
if (_crabSmartApi) return _crabSmartApi;

const chain = require('../config/chain.safe.json').crab_smart;
_crabSmartApi = new Web3(chain.endpoint);
return _crabSmartApi;
}


class TransferReceipt {
tx: String;
preview: String;
}
12 changes: 12 additions & 0 deletions airdrop/api/authorization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type {VercelRequest, VercelResponse} from '@vercel/node';
import * as qs from 'qs';


export default async function (req: VercelRequest, res: VercelResponse) {
res.statusCode = 302;
const auth = qs.parse(req.query);
const accessToken = auth.access_token;
res.setHeader('Set-Cookie', `x-access-token=${accessToken};httpOnly;secure;path=/;`);
res.setHeader('Location', '/');
res.end('Redirect /');
}
2 changes: 2 additions & 0 deletions airdrop/api/config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
config
===
7 changes: 7 additions & 0 deletions airdrop/api/config/chain.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"crab_smart": {
"endpoint": "https://crab-rpc.darwinia.network",
"address": "0xe485DfCDA94dD8F04E12F76D1b98cB0D33273758",
"seed": "{{ crab_smart_seed }}"
}
}
14 changes: 14 additions & 0 deletions airdrop/api/config/grant.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"defaults": {
"origin": "https://airdrop.darwinia.network",
"transport": "querystring",
"state": true
},
"github": {
"key": "{{ github_oauth_app_key }}",
"secret": "{{ github_oauth_app_secret }}",
"scope": ["user"],
"callback": "/api/authorization",
"overrides": {}
}
}
5 changes: 5 additions & 0 deletions airdrop/api/config/redis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"host": "{{ redis_host }}",
"port": "{{ redis_port }}",
"password": "{{ redis_password }}"
}
Loading

0 comments on commit dce795e

Please sign in to comment.