Skip to content

Commit

Permalink
[Draft] Implement phase 3 of offramp prototype (#22)
Browse files Browse the repository at this point in the history
* first steps

* WIP

* first iteration prototype ui

* some UI changes for events, input box

* remove mock testing code

* Add `.husky` files to fix precommit hook

* Remove linting from `lint-staged`

* Run `yarn format`

* wrap log text

* Break text in event boxes

* align event text

* lint issues, cleaner stellar error text display

* more lint ts changes

* Fix 'no-async-promise-executor' issue

* Fix missing hook deps

* Fix bad refactoring of promise

* don't use iframe

* different big number library that handles decimals also

* print ephemeral secret

* load ephemeral account only once for transaction creation

* remove unused bignumber libraries

* addition of wallet connect

* check balance before start offramp

* override type error with metamask service

* use encoded address for comparisson with redeem event

* patch diff for testing configuratons

* Add basic test for VaultService redeem function

* Small refactor

* Amend test

* Add handling for walletaccount and keypair as signer

* Finish test

* Refactor code

* Rename `Api` to `ApiComponents`

* Fix wallet connect dialog not showing
by changing the used polyfills

* Small refactor

* Fix `build` not working
by also defining the target to `esnext`

* add signatures request from server

* testing code config

* adding stellar signing express service

* remove mock values

* cors allow all origins

* run yarn format

* use bn.js to replace big.js, modify number parser to accept a string decimal number

* rename component files

* add signing service status check before process begins

* modify readme

* fix typo bug

* use service local at port 3000

* some lint changes, use only secret env variable

* removed unused big.js import

* simplify response

* yarn format

* remove env variables from memcache

* point to deployed signer service backend

---------

Co-authored-by: Gianfranco <g.tasteri@gmail.com>
  • Loading branch information
ebma and gianfra-t authored May 2, 2024
1 parent 28f2e76 commit 72771eb
Show file tree
Hide file tree
Showing 55 changed files with 11,556 additions and 262 deletions.
11 changes: 4 additions & 7 deletions App.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
html, body {
html,
body {
margin: 0;
padding: 0;
width: 100%;
Expand All @@ -8,15 +9,15 @@ html, body {
overflow-x: hidden;

/* Important to make sure the WalletConnect dialog is shown on top of the others */
--wcm-z-index: 1000;}
--wcm-z-index: 1000;
}

#app {
width: 100%;
min-width: 1800px;
box-sizing: border-box;
}


.inputBox {
width: 40%;
margin: 20px auto;
Expand Down Expand Up @@ -73,7 +74,6 @@ input {
border-color: #ffffff;
}


.eventBox.success {
background-color: #d4edda;
border-color: #c3e6cb;
Expand Down Expand Up @@ -180,12 +180,10 @@ button:hover {
padding: 0.5rem 1rem;
}


.modal-select {
max-height: 600px;
}


/* Fix wallet select modal to allow for scrolling */
#react-portal-modal-container main {
overflow-y: auto;
Expand All @@ -194,4 +192,3 @@ button:hover {
#react-portal-modal-container > div > div {
max-height: 90vh;
}

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"@tanstack/react-query": "^5.31.0",
"@walletconnect/modal": "^2.6.2",
"@walletconnect/universal-provider": "^2.12.2",
"big.js": "^6.2.1",
"bn.js": "^5.2.1",
"buffer": "^6.0.3",
"preact": "^10.12.1",
"react-daisyui": "^5.0.0",
Expand All @@ -65,7 +65,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/preact": "^3.2.3",
"@testing-library/preact-hooks": "^1.1.0",
"@types/big.js": "^6",
"@types/bn.js": "^5",
"@types/jest": "^29.4.0",
"@types/node": "^18.14.1",
"@types/react": "^18.0.28",
Expand Down
13 changes: 13 additions & 0 deletions signer-service/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules/
.git/
.vscode/
coverage/

.dockerignore
Dockerfile

.gitignore
.env
.eslintrc
.editorconfig
.gitlab-ci.yml
13 changes: 13 additions & 0 deletions signer-service/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
# trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
5 changes: 5 additions & 0 deletions signer-service/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
NODE_ENV=development
PORT=3000
RATE_LIMIT_WINDOW_MINUTES=15
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_NUMBER_OF_PROXIES=1
5 changes: 5 additions & 0 deletions signer-service/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
NODE_ENV=development
PORT=3000
RATE_LIMIT_WINDOW_MINUTES=15
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_NUMBER_OF_PROXIES=1
18 changes: 18 additions & 0 deletions signer-service/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"rules": {
"no-console": 0,
"no-underscore-dangle": 0,
"no-unused-vars": ["error", { "argsIgnorePattern": "next" }],
"no-use-before-define": ["error", { "variables": false }],
"no-multi-str": 0
},
"env": {
"es2020": true,
"node": true,
"mocha": true
},
"parserOptions": {
"ecmaVersion": 8
},
"extends": ["airbnb-base", "prettier"]
}
46 changes: 46 additions & 0 deletions signer-service/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Use yarn.lock instead
package-lock.json

# Environment variables
.env

# Logs
logs
*.log
npm-debug.log*

# Documentation
docs

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history
1 change: 1 addition & 0 deletions signer-service/.yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
14 changes: 14 additions & 0 deletions signer-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM node:8-alpine

EXPOSE 3000

ARG NODE_ENV
ENV NODE_ENV $NODE_ENV

RUN mkdir /app
WORKDIR /app
ADD package.json yarn.lock /app/
RUN yarn --pure-lockfile
ADD . /app

CMD ["yarn", "docker:start"]
23 changes: 23 additions & 0 deletions signer-service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
### ABOUT

This simple server provides the signature of the corresponding transactions for the ephemeral account to be created,
perform the offramping payment and be closed (merging it with the funding account).

The signature for the creation transaction can be obtainid by requesting at `URL/v1/stellar/create`, while the two
transactions for payment and merge operation are returned at `URL/v1/stellar/payment`.

Run to start the service.

### Running

For production `FUNDING_SECRET='{FUNDING_ACCOUNT_SECRET_KEY}' yarn start`

For debelopment `FUNDING_SECRET='{FUNDING_ACCOUNT_SECRET_KEY}' yarn dev`

### Available environment variables

- `NODE_ENV` - The environment the application is running in, default is `production`
- `PORT` - The port the HTTP server will listen on, default is `3000`
- `RATE_LIMIT_MAX_REQUESTS` - The maximum number of requests per IP address, default is `100`
- `RATE_LIMIT_WINDOW_MINUTES` - The time window in minutes for the rate limit, default is `15` minutes
- `RATE_LIMIT_NUMBER_OF_PROXIES` - The number of proxies between server and user, default is `1`
79 changes: 79 additions & 0 deletions signer-service/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"name": "@pendulum-network/token-api",
"version": "1.0.0",
"description": "",
"author": "Pendulum Chain",
"main": "src/index.js",
"license": "MIT",
"engines": {
"node": ">=12",
"yarn": "*"
},
"scripts": {
"precommit": "yarn lint-staged && yarn lint",
"prestart": "yarn docs",
"start": "cross-env NODE_ENV=production pm2 start ./src/index.js",
"dev": "nodemon ./src/index.js",
"lint-staged": "prettier --write --ignore-unknown",
"lint": "eslint ./src/ --ignore-path .gitignore --ignore-pattern internals/scripts",
"lint:fix": "yarn lint --fix",
"lint:watch": "yarn lint --watch",
"test": "echo \"No tests defined yet\" && exit 0",
"test:cov": "echo \"No tests defined yet\" && exit 0",
"validate": "yarn lint && yarn test",
"postpublish": "git push --tags",
"docs": "apidoc -i src -o docs",
"postdocs": "open-cli docs/index.html"
},
"dependencies": {
"@polkadot/api": "^10.3.4",
"bcryptjs": "2.4.3",
"bluebird": "^3.5.0",
"body-parser": "^1.17.0",
"compression": "^1.6.2",
"cors": "^2.8.3",
"cross-env": "^7.0.3",
"dotenv-safe": "^8.2.0",
"email-templates": "^6.0.3",
"express": "^4.15.2",
"express-rate-limit": "^6.7.0",
"express-validation": "^1.0.2",
"helmet": "^4.6.0",
"http-status": "^1.0.1",
"joi": "^10.4.1",
"jwt-simple": "0.5.6",
"lodash": "^4.17.4",
"method-override": "^3.0.0",
"moment-timezone": "^0.5.13",
"mongoose": "^5.2.17",
"morgan": "^1.8.1",
"nodemailer": "^6.3.1",
"passport": "^0.4.0",
"passport-http-bearer": "^1.0.1",
"passport-jwt": "4.0.0",
"pm2": "^5.1.0",
"pug": "^3.0.1",
"stellar-sdk": "^11.3.0",
"uuid": "^3.1.0",
"winston": "^3.1.0"
},
"devDependencies": {
"apidoc": "^0.28.1",
"chai": "^4.1.0",
"chai-as-promised": "^7.1.1",
"coveralls": "^3.0.0",
"eslint": "^7.29.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.2.0",
"husky": "^3.0.7",
"mocha": "^6.2.2",
"nodemon": "^2.0.1",
"nyc": "^15.1.0",
"opn-cli": "^5.0.0",
"prettier": "^2.8.7",
"sinon": "^7.5.0",
"sinon-chai": "^3.0.0",
"supertest": "^6.1.3"
}
}
56 changes: 56 additions & 0 deletions signer-service/src/api/controllers/stellar.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require('dotenv').config();

const { Horizon, Keypair } = require('stellar-sdk');
const { HORIZON_URL } = require('../../constants/constants');
const FUNDING_SECRET = process.env.FUNDING_SECRET;

const { buildCreationStellarTx, buildPaymentAndMergeTx } = require('../services/stellar.service');

const horizonServer = new Horizon.Server(HORIZON_URL);
// Derive funding pk
const FUNDING_PUBLIC_KEY = Keypair.fromSecret(FUNDING_SECRET).publicKey();

exports.sendStatusWithPk = async (req, res, next) => {
try {
//ensure the fundign account exists
let account = await horizonServer.loadAccount(FUNDING_PUBLIC_KEY);
let stellarBalance = account.balances.find((balance) => balance.asset_type === 'native');

// ensure we have at the very least 2.5 XLM in the account
if (Number(stellarBalance.balance) < 2.5) {
return res.json({ status: false, public: FUNDING_PUBLIC_KEY });
}

return res.json({ status: true, public: FUNDING_PUBLIC_KEY });
} catch (error) {
console.error('Server error:', error);
return res.status(500).json({ error: 'Server error', details: error.message });
}
};

exports.createStellarTransaction = async (req, res, next) => {
try {
let { signature, sequence } = await buildCreationStellarTx(FUNDING_SECRET, req.body.accountId, req.body.maxTime);
return res.json({ signature, sequence, public: FUNDING_PUBLIC_KEY });
} catch (error) {
console.error('Error in createStellarTransaction:', error);
return res.status(500).json({ error: 'Failed to create transaction', details: error.message });
}
};

exports.changeOpTransaction = async (req, res, next) => {
try {
console.log(req.body);
let { signature } = await buildPaymentAndMergeTx(
FUNDING_SECRET,
req.body.accountId,
req.body.sequence,
req.body.paymentData,
req.body.maxTime,
);
return res.json({ signature, public: FUNDING_PUBLIC_KEY });
} catch (error) {
console.error('Error in changeOpTransaction:', error);
return res.status(500).json({ error: 'Failed to process transaction', details: error.message });
}
};
26 changes: 26 additions & 0 deletions signer-service/src/api/errors/api-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const httpStatus = require('http-status');
const ExtendableError = require('./extendable-error');

/**
* Class representing an API error.
* @extends ExtendableError
*/
class APIError extends ExtendableError {
/**
* Creates an API error.
* @param {string} message - Error message.
* @param {number} status - HTTP status code of error.
* @param {boolean} isPublic - Whether the message should be visible to user or not.
*/
constructor({ message, errors, stack, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false }) {
super({
message,
errors,
status,
isPublic,
stack,
});
}
}

module.exports = APIError;
18 changes: 18 additions & 0 deletions signer-service/src/api/errors/extendable-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @extends Error
*/
class ExtendableError extends Error {
constructor({ message, errors, status, isPublic, stack }) {
super(message);
this.name = this.constructor.name;
this.message = message;
this.errors = errors;
this.status = status;
this.isPublic = isPublic;
this.isOperational = true; // This is required since bluebird 4 doesn't append it anymore.
this.stack = stack;
// Error.captureStackTrace(this, this.constructor.name);
}
}

module.exports = ExtendableError;
Loading

0 comments on commit 72771eb

Please sign in to comment.