Compare commits

...
11 Commits
Author SHA1 Message Date
Chris Troutner 5ede934be5 Merge pull request #87 from Permissionless-Software-Foundation/ct-unstable
fix(wallet adapter): Binding 'this' object to class methods
2025-04-26 14:36:28 -07:00
Chris Troutner 8d036ea9ba fix(wallet adapter): Binding 'this' object to class methods 2025-04-26 14:30:54 -07:00
Chris Troutner bc6acc846a Merge pull request #86 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream
2025-04-26 14:13:25 -07:00
Chris Troutner afa050261f Merge remote-tracking branch 'upstream/master' into merge-upstream 2025-04-26 14:11:39 -07:00
Chris Troutner 1715d064fb Merge pull request #85 from Permissionless-Software-Foundation/dh-user-mnemonic
feat(user): Added mnemonic to user model
2025-04-26 12:35:37 -07:00
Daniel Gonzalez cf2f2af90f feat(user): Added mnemonic to user model 2025-04-24 12:45:55 -04:00
Chris Troutner 354321c956 Merge pull request #173 from Permissionless-Software-Foundation/ct-unstable
fix(wallet.adapter): Binding this object to all methods
2025-04-22 19:07:06 -07:00
Chris Troutner af2572a493 fix(wallet.adapter): Binding this object to all methods 2025-04-22 18:58:03 -07:00
Chris Troutner 1a5dadbf35 Merge pull request #84 from Permissionless-Software-Foundation/ct-unstable
fix(README): Updating README details
2025-04-11 16:18:37 -07:00
Chris Troutner ced7a60f14 More README edits 2025-04-11 16:15:29 -07:00
Chris Troutner 043c4a8b2b fix(README): Updating README details 2025-04-11 16:09:31 -07:00
9 changed files with 91 additions and 38 deletions
+12 -34
View File
@@ -1,14 +1,14 @@
# bch-dex
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![Join the chat at https://gitter.im/Permissionless-Software-Foundation/psf-dex-dev](https://badges.gitter.im/Permissionless-Software-Foundation/psf-dex-dev.svg)](https://gitter.im/Permissionless-Software-Foundation/psf-dex-dev?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
`bch-dex` is a decentralized exchange for permissionless trading of [SLP tokens](https://github.com/simpleledger/slp-specifications/blob/master/slp-token-type-1.md) and [NFTs](https://github.com/simpleledger/slp-specifications/blob/master/slp-nft-1.md). Trading fees are incredibly small, compared to other DEX platforms. The software is based entirely on open protocols and open source software. It's not a '.com' website that you visit, but software you run on your own computer, in the confort of your own home. So long as you have an internet connection and the technical knowledge to run this software, no one can stop you from trading tokens. `bch-dex` software is maintained by the [Permissionless Software Foundation](https://psfoundation.cash/).
`bch-dex` is a decentralized exchange *protocol* for permissionless trading of [SLP tokens](https://github.com/simpleledger/slp-specifications/blob/master/slp-token-type-1.md) and [NFTs](https://github.com/simpleledger/slp-specifications/blob/master/slp-nft-1.md). Trading fees are incredibly small, compared to other DEX platforms. The software is based entirely on open protocols and open source software. `bch-dex` software is maintained by the [Permissionless Software Foundation](https://psfoundation.info).
- [High-level Overview of bch-dex](https://youtu.be/LVX8CLi4sHw) (Video)
This is a prototype web service that monitors the [P2WDB](https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service) for trading signals, to trade BCH and SLP tokens. It's inspired by the [SWaP Protocol](https://github.com/vinarmani/swap-protocol/blob/master/swap-protocol-spec.md).
This is a prototype web service that monitors one or more [Nostr relays](https://nostr.com) for trading signals, to trade BCH and SLP tokens. It's inspired by the [SWaP Protocol](https://github.com/vinarmani/swap-protocol/blob/master/swap-protocol-spec.md).
This repository contains the back end code. The user interface is contained in the [bch-dex-ui](https://github.com/Permissionless-Software-Foundation/bch-dex-ui) repository.
This repository contains the back end code. The user interface is contained in the [bch-dex-ui-v3](https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v3) repository.
**Warning**: This repository is under active development. Things will be constantly changing and breaking.
@@ -19,24 +19,17 @@ This repository contains the back end code. The user interface is contained in t
## Participate
This is an open source project, and we encourage other JavaScript developers to participate in its creation and maintenance. We have two chat rooms for the community:
- [Telegram Channel](https://t.me/psf_dex_dev)
- [Gitter Channel](https://gitter.im/Permissionless-Software-Foundation/psf-dex-dev)
- [Telegram Channel](https://t.me/bch_js_toolkit)
## Installation
Running the DEX requires composition of these different software packages:
- bch-dex - This repository is the back end software that tracks trade data on the network, generates [Offers and Counter Offers](https://github.com/Permissionless-Software-Foundation/bch-dex/tree/ct-unstable/dev-docs#definitions), and finalizes trades by accepting Counter Offers.
- [bch-dex-ui](https://github.com/Permissionless-Software-Foundation/bch-dex-ui) is a [Gatsby](https://www.gatsbyjs.com/) web app and user interface (UI) for bch-dex.
- [P2WDB](https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service) is a censorship-resistant database used to communicate trade data between peers running bch-dex.
- [IPFS](https://ipfs.io/) is a censorship-resistant network for communicating data over the internet.
- [MongoDB](https://www.mongodb.com/) is a database used by both P2WDB and bch-dex to store and manage local data.
- [bch-dex-ui-v3](https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v3) web app and user interface (UI) for bch-dex focused on Sellers of tokens.
- [MongoDB](https://www.mongodb.com/) is a database used by bch-dex to store and manage local data, user accounts, etc.
The above software is orchestrated using [Docker](https://www.docker.com/) and Docker Compose. The target operating system is Ubuntu 20+, and the target hardware is amd64 (normal desktop PCs) and the arm64 (Raspberry Pi 4). Trying to operate this software on other operating systems or hardware is possible, but not supported.
The above software is orchestrated using [Docker](https://www.docker.com/) and Docker Compose. The target operating system is Ubuntu 20+, and the target hardware is amd64 (normal desktop PCs). Trying to operate this software on other operating systems or hardware is possible, but not supported.
The Raspberry Pi 4 is the primary hardware target, because this software functions best when it is always running. The Pi 4 makes a great appliance for monitoring trade signals and finalizing trades.
Instructions for setting up Node.js, Docker, and Docker Compose can be found in [this Gist](https://gist.github.com/christroutner/a39f656850dc022b60f25c9663dd1cdd). Walk-through videos can also be found on the [PSF Videos page](https://psfoundation.cash/video/).
Here are the steps involved in setting up bch-dex:
Setup instructions:
1. Follow the direction in [this Gist](https://gist.github.com/christroutner/a39f656850dc022b60f25c9663dd1cdd) to install Node.js, Docker, and Docker Compose.
1. Clone the repository with `git clone https://github.com/Permissionless-Software-Foundation/bch-dex` and enter it with `cd bch-dex`.
@@ -44,32 +37,17 @@ Here are the steps involved in setting up bch-dex:
1. Create a wallet:
- `cd production/scripts`
- `node create-wallet.js`
1. Change directory to the `production/docker` or `production/rpi-docker` folder depending on your hardware target.
1. Change directory to the `production/docker` folder.
1. Pull the Docker images down from Docker Hub: `docker-compose pull`
1. Build the core software: `docker-compose build --no-cache`
1. Start the Docker containers with `docker-compose up -d`
1. Wait for the P2WDB to sync and populate bch-dex with trade data. You can monitor it with `docker logs --tail 20 -f p2wdb`.
1. Open a web browser and navigate the `http://localhost:4500`. You'll be able to see new Offers as they come in and are detected by bch-dex.
1. To take the other side of the trade, click the `Take` button in the UI.
1. You can add the 12-word mnemonic from the `wallet.json` file to the the web wallet, which will mirror your wallet in the UI, and allow you to perform basic wallet functions (send and receive BCH and tokens).
## Blockchain Infrastructure
The P2WDB and bch-dex containers requires a 'Global Back End' (as described in [CashStack.info](https://cashstack.info)) in order to connect to the blockchain. By default, the Docker containers connect to [free-bch.fullstack.cash](https://free-bch.fullstack.cash/). Several community-provided Global Back End servers are provided and can be [viewed here](https://gist.github.com/christroutner/63c5513782181f8b8ea3eb89f7cadeb6). The back end can be changed by setting the `CONSUMER_URL` environment variable in the `docker-compose.yml` file.
### Applying Software Updates
As this is an active project, software updates will happen frequently. To apply a software update, perform these steps.
1. Enter the `docker` or `rpi-docker` folder, depending on your hardware target.
1. Bring down the Docker containers with `docker-compose down`.
1. Pull in the software updates with `git pull`
1. Update dependencies with `npm install`
1. Pull new Docker images with `docker-compose pull`
1. Clean up disk space by deleting old Docker images with `./cleanup-images.sh`
1. Start the Docker containers with `docker-compose up -d`
Sometimes it may be necessary to delete the databases before applying a software update. This can be done by stopping the Docker containers and deleting the `production/data` directory. When the Docker containers are restarted, they will recreate that directory. The P2WDB will re-sync and bch-dex will be populated with fresh trade data.
bch-dex requires a [Cash Stack](https://cashstack.info) back end in order to connect to the blockchain. By default, the Docker containers connect to [free-bch.fullstack.cash](https://free-bch.fullstack.cash/). Several community-provided servers are provided and can be [viewed here](https://consumers.psfoundation.info/consumers.json). The back end can be changed by setting the `CONSUMER_URL` environment variable in the `docker-compose.yml` file.
## License
+1
View File
@@ -11,6 +11,7 @@ const User = new mongoose.Schema({
name: { type: String },
username: { type: String },
password: { type: String, required: true },
mnemonic: { type: String, required: true },
email: {
type: String,
required: true,
+7
View File
@@ -20,6 +20,13 @@ class Wallet {
// Bind 'this' object to all subfunctions
this.instanceWalletWithoutInitialization = this.instanceWalletWithoutInitialization.bind(this)
this._instanceWallet = this._instanceWallet.bind(this)
this.openWallet = this.openWallet.bind(this)
this.instanceWallet = this.instanceWallet.bind(this)
this.incrementNextAddress = this.incrementNextAddress.bind(this)
this.getKeyPair = this.getKeyPair.bind(this)
this.optimize = this.optimize.bind(this)
this.getBalance = this.getBalance.bind(this)
}
// This is used for initializing the wallet, without waiting to update the wallet
+12
View File
@@ -1,5 +1,7 @@
/*
Adapter library for working with a wallet.
This library is specific to bch-dex. The other wallet adapter library is
inherited from ipfs-service-provider.
*/
// Public npm libraries
@@ -41,7 +43,17 @@ class WalletAdapter {
})
// Bind the 'this' object
this.openWallet = this.openWallet.bind(this)
this.instanceWallet = this.instanceWallet.bind(this)
this.incrementNextAddress = this.incrementNextAddress.bind(this)
this.getKeyPair = this.getKeyPair.bind(this)
this.generateSignature = this.generateSignature.bind(this)
this.generatePartialTx = this.generatePartialTx.bind(this)
this.moveTokens = this.moveTokens.bind(this)
this.moveBch = this.moveBch.bind(this)
this.deseralizeTx = this.deseralizeTx.bind(this)
this.completeTx = this.completeTx.bind(this)
this.reclaimTokens = this.reclaimTokens.bind(this)
}
// Open the wallet file, or create one if the file doesn't exist.
+10 -1
View File
@@ -16,7 +16,7 @@ class UserLib {
'Instance of adapters must be passed in when instantiating User Use Cases library.'
)
}
this.BchWallet = this.adapters.wallet.BchWallet
// Encapsulate dependencies
this.UserEntity = new UserEntity()
this.UserModel = this.adapters.localdb.Users
@@ -30,6 +30,11 @@ class UserLib {
const userEntity = this.UserEntity.validate(userObj)
const user = new this.UserModel(userEntity)
const wallet = new this.BchWallet()
const walletInfo = await wallet.walletInfoPromise
const mnemonic = walletInfo.mnemonic
user.mnemonic = mnemonic
// Enforce default value of 'user'
user.type = 'user'
// console.log('user: ', user)
@@ -102,6 +107,7 @@ class UserLib {
// Input Validation
// Optional inputs, but they must be strings if included.
if (newData.email && typeof newData.email !== 'string') {
throw new Error("Property 'email' must be a string!")
}
@@ -111,6 +117,9 @@ class UserLib {
if (newData.password && typeof newData.password !== 'string') {
throw new Error("Property 'password' must be a string!")
}
if (newData.mnemonic) {
throw new Error("Property 'mnemonic' cannot be updated!")
}
// Save a copy of the original user type.
const userType = existingUser.type
+3 -1
View File
@@ -173,6 +173,7 @@ if (!config.noMongo) {
)
assert.property(result.data, 'token', 'Token property exists.')
assert.equal(result.data.user.type, 'user')
assert.property(result.data.user, 'mnemonic')
})
})
@@ -265,7 +266,7 @@ if (!config.noMongo) {
const users = result.data.users
// console.log(`users: ${util.inspect(users)}`)
assert.hasAnyKeys(users[0], ['type', '_id', 'email'])
assert.hasAnyKeys(users[0], ['type', '_id', 'email', 'mnemonic'])
assert.isNumber(users.length)
})
@@ -375,6 +376,7 @@ if (!config.noMongo) {
assert.property(user, 'type')
assert.property(user, 'email')
assert.property(user, 'mnemonic')
assert.property(user, '_id')
assert.equal(user._id, _id)
+2 -1
View File
@@ -31,7 +31,8 @@ describe('#User-Adapter', () => {
testuser = new User({
email: 'test983@test.com',
name: 'test983',
password: 'password'
password: 'password',
mnemonic: '12 words mnemonic'
})
})
+1
View File
@@ -150,6 +150,7 @@ const wallet = {
return { cashAddress: 'fakeAddr', wif: 'fakeWif', hdIndex: 1 }
},
bchWallet: new MockBchWallet(),
BchWallet: MockBchWallet,
moveTokens: async () => {},
moveBch: async () => {},
reclaimTokens: async ()=>{},
+43 -1
View File
@@ -117,6 +117,29 @@ describe('#users-use-case', () => {
assert.include(err.message, 'test error')
}
})
it('should handle wallet errors', async () => {
try {
// Force an error with the database.
class MockErrorBchWallet {
constructor () {
this.walletInfoPromise = Promise.reject(new Error('test error'))
}
}
uut.BchWallet = MockErrorBchWallet
const usrObj = {
email: 'test@test.com',
password: 'password',
name: 'test'
}
await uut.createUser(usrObj)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should create a new user in the DB', async () => {
// Note: The user created in this test is used by the getUser, update,
@@ -129,7 +152,6 @@ describe('#users-use-case', () => {
}
const { userData, token } = await uut.createUser(usrObj)
testUser = userData
// Commented out because there is some sophisticated mocking required that
@@ -327,6 +349,26 @@ describe('#users-use-case', () => {
)
}
})
it('should throw an error if mnemonic is provided', async () => {
try {
const newData = {
email: 'test@test.com',
password: 'password',
name: 'test',
mnemonic: 'test'
}
await uut.updateUser(testUser, newData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(
err.message,
"Property 'mnemonic' cannot be updated!"
)
}
})
it('should update the user model', async () => {
const newData = {