NBA Top Shot已經向其80萬用戶售賣了超過3億美元的NFT,索斯比則剛剛以1700萬美元的價格拍出 一幅數字藝術品。當你購買一個NBA Top Shot藏品時,你並沒有獲得唯一的商業權力,你甚至不能獨享其權力。 實際上那幅1700萬美元的藝術品,你可以免費觀賞。
不過讓我們探討下NFT帶來的價值:資產具有密碼學算法可驗證的所有權以及合約賦予的售賣或轉讓能力。
很多類型的資產可以受益於密碼學可驗證的所有權:成績單、證書、知識產權等。想象一下用 區塊鏈代替USPTO… 可以跳過律師直接提交你的申請,先到先服務。
是一個朋友的建議讓我看是考慮將門票作為一種NFT資產,這很有意義。票務市場最大的問題 是什么?下面是一些:
- 防偽
- 大量的交易手續費。我們都知道當你看到50美元的門票卻需要花費72.50美元以便覆蓋售票 中介的成本時的感覺
- 不受監管的二級市場
如果我們使用智能合約來管理資產,這些問題就會消失。確定NFT的真實性是小事一樁,交易 手續費也可以通過采納區塊鏈得到大幅削減。
對我而言,最令人興奮的一點是票務發行人能夠設置二級市場條款。你可以讓你的資產不可 轉讓,確保只能低於標價出售,甚至在任何二次銷售時幫助表演者削減成本。
在這個去中心化系統中,每個人都可以得到更公平透明的體驗。好了,讓我們開始這個系統的開發。
1、門票NFT的區塊鏈平台選擇:以太坊 vs. Flow
我們需要做出的第一個決定,是選擇使用哪個區塊鏈平台。
我們可以使用以太坊,但是交易手續費有點高,雖然在未來的ETH 2.0升級后手續費可能 顯著下降。
Flow是一個開發者友好的新型區塊鏈生態,手續費微乎其微,聽起來是一個好的選擇。
實際上Dapper Lab的NBA Top Shot就是使用Flow智能合約,和我們下面要部署的合約沒有太多區別。
從較高的層面講,下面就是我們需要構造的一個基本但可用的票據集市,我們沒有實現完整 的買/賣功能,不過這可能是下一個教程的主題。
- 在我們的Flow智能合約中定義票證的不可轉讓條款
- 創建虛擬賬號以便發行人和參與者能夠訪問NFT
- 使用交易來安全地展示常見功能,例如鑄造和票證轉讓
- 使用React.js實現一個簡單的前端web界面
2、Flow區塊鏈開發環境設置
我利用Flow文檔中的教程來熟悉其智能合約編程語言Cadence以及其標准的NFT模板。 如果你打算遵循這一學習路徑,則需要安裝Flow CLI。
當然我們可以在Flow主網或測試網部署合約,但是我們講利用FLow仿真器來進行快速本地開發。 使用如下命令啟動仿真器:
3、Flow區塊鏈門票NFT智能合約開發
我們的非同質化票證智能合約需要定義NFT的特點以及鑄造、存儲、轉讓等函數。其中某些 功能需要公開可用,例如存入或獲取元數據,而另一些功能例如提取和鑄造,則需要一定的 權限才可以執行。
我也希望確認我們的票證是不可轉讓的,因此我們需要設置必要的檢查條件以便禁止多次存入。 下面看一下我們的Cadence智能合約。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
// NonFungibleTicket.cdc // contract to manage unique tradeable tickets (based on Flow NFT standard) // see: https://docs.onflow.org/cadence/tutorial/04-non-fungible-tokens/
pub contract NonFungibleTicket {
// set up a couple events for key deposits/withdrawals pub event Withdraw(id: UInt64, from: Address?) pub event Deposit(id: UInt64, to: Address?)
// our NFT (Non-Fungible-Ticket) is simply defined by an id. metadata coming later pub resource NFT { pub let id: UInt64 // we want to make our ticket non-transferrable, so let's keep track of how many times it has changed hands pub var numTransfers : UInt64
init(initID: UInt64) { self.id = initID self.numTransfers = 0 } // we will need a function to iterate the number of transfers each time pub fun transfer() { self.numTransfers = self.numTransfers + 1 as UInt64 } }
// receiver interface allows others to interact w certain functions via public access pub resource interface NFTReceiver { pub fun deposit(token: @NFT, metadata: {String : String}) pub fun getIDs(): [UInt64] pub fun idExists(id: UInt64): Bool pub fun getMetadata(id: UInt64) : {String : String} // obviously, we don't allow public access to withdraw/minting functions }
// defining a Collection resource for all our tickets pub resource Collection: NFTReceiver { pub var ownedNFTs: @{UInt64: NFT} pub var metadataObjs: {UInt64: { String : String }}
init () { self.ownedNFTs <- {} self.metadataObjs = {} }
// withdraw forced to be non-nil. function throws error if NFT with withdrawID doesn't exist pub fun withdraw(withdrawID: UInt64): @NFT { let token <- self.ownedNFTs.remove(key: withdrawID)!
emit Withdraw(id: token.id, from: self.owner?.address)
return <- token }
// the deposit function is a bit more complex. first of all, this is where the metadata comes in: pub fun deposit(token: @NFT, metadata: {String : String}) { // our token can be transferred no more than once (from admin to attendee) if token.numTransfers > (1 as UInt64) { panic("Ticket is non-transferrable!") } self.metadataObjs[token.id] = metadata
emit Deposit(id: token.id, to: self.owner?.address) // log the transfer (increases numTransfers by 1) token.transfer() self.ownedNFTs[token.id] <-! token }
// rest of these are pretty straightforward pub fun idExists(id: UInt64): Bool { return self.ownedNFTs[id] != nil }
pub fun getIDs(): [UInt64] { return self.ownedNFTs.keys }
pub fun updateMetadata(id: UInt64, metadata: {String: String}) { self.metadataObjs[id] = metadata }
pub fun getMetadata(id: UInt64): {String : String} { return self.metadataObjs[id]! }
destroy() { destroy self.ownedNFTs } }
// will need to create an empty collection for any account that wants our NFT pub fun createEmptyCollection(): @Collection { return <- create Collection() }
// can explicitly share NFTMinter resource with another admin so that they can mint tickets pub resource NFTMinter { pub var idCount: UInt64
init() { self.idCount = 1 }
pub fun mintNFT(): @NFT { var newNFT <- create NFT(initID: self.idCount) self.idCount = self.idCount + 1 as UInt64 return <-newNFT }
}
// launching the contract does 3 things: init() { // 1) save a fresh collection to the admin's storage self.account.save(<-self.createEmptyCollection(), to: /storage/NFTCollection)
// 2) allow public access to NFTReceiver functions through this reference self.account.link<&{NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection)
// 3) save NFTMinter resource to private storage self.account.save(<-create NFTMinter(), to: /storage/NFTMinter) }
}
|
關於Cadence,Flow官方文檔要比我介紹的更清楚,不過在更高的層面來說,Cadence使用 Resources(資源)和Capabilities(能力)來定義誰(Who)可以訪問什么(What)功能。
例如,我們講NFTCollection和NFTMinter資源存入部署賬號的/storage/
路徑,這意味着 這些資源是私有的。但是我們在/public/
路徑下發布一個指向NFTReceiver能力的鏈接。 另外需要注意的是,我們的NFT只是簡單的利用其整數ID定義,並采用一個numTransfers計數器 來記錄NFT的存入次數。
在這個示例中,如果某人試圖再次轉讓我們的票證,交易將失敗。將合約存入名為cadence/contracts/
的目錄。
在我們部署合約之前,我們需要創建flow.json文檔來指定誰(Who)在哪里(Where)部署什么(What)。 在項目目錄中執行以下命令初始化這個文件:
這會給我們一個啟動賬號以及相應的私鑰。稍后我們將查看flow.json文件,但是首先我們需要為 參與者創建一個賬號。運行下面的代碼來生成密鑰對:
保存上述命令生成的密鑰對,然后運行:
1
|
flow accounts create ATTENDEE_PUB_KEY
|
將ATTENDEE_PUB_KEY替換為你剛剛生成的公鑰。
記錄下來0x開頭的地址。現在我們具備了flow.json需要的所有資料。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
{ "emulators": { "default": { "port": 3569, "serviceAccount": "emulator-account" } }, "contracts": { "NonFungibleTicket": "./cadence/contracts/NonFungibleTicket.cdc" }, "networks": { "emulator": "127.0.0.1:3569", "mainnet": "access.mainnet.nodes.onflow.org:9000", "testnet": "access.devnet.nodes.onflow.org:9000" }, "accounts": { "emulator-account": { "address": "f8d6e0586b0a20c7", "keys": "e61fd9cbcf7d7d0918c5d02f79c9be08717ca82b5e7bd8c151e009eeb384bb78" }, "attendee-account": { "address": "01cf0e2f2f715450", "keys": "ATTENDEE_PRIVATE_KEY" } }, "deployments": { "emulator": { "emulator-account": ["NonFungibleTicket"] } } }
|
注意:永遠不要共享你的私鑰。
你將看到我們在NonFungibleTicket合約中添加了一個指針(Pointer),我們的新的參與者賬號 以及仿真器賬號(標識我們的票證發行人)的合約部署。現在我們可以用下面的命令部署合約:
如果一切順利,你將會看到下面這樣的輸出:
1 2 3
|
Deploying 1 contracts for accounts: emulator-account NonFungibleTicket -> 0xf8d6e0586b0a20c7 ✨ All contracts deployed successfully
|
4、鑄造Flow區塊鏈的門票NFT
現在是時候創建我們的第一個NFT了。下面我們將使用Cadence,但是我們將使用交易而不是定義 一個合約。交易是我們使用智能合約中定義的函數的方法,交易執行將導致區塊鏈狀態的變化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
import NonFungibleTicket from 0xf8d6e0586b0a20c7
// This transaction allows the Minter account to mint an NFT // and deposit it into its collection.
transaction {
// The reference to the collection that will be receiving the NFT let receiverRef: &{NonFungibleTicket.NFTReceiver}
// The reference to the Minter resource stored in account storage let minterRef: &NonFungibleTicket.NFTMinter
prepare(acct: AuthAccount) { // Get the owner's collection capability and borrow a reference self.receiverRef = acct.getCapability<&{NonFungibleTicket.NFTReceiver}>(/public/NFTReceiver) .borrow() ?? panic("Could not borrow receiver reference")
// Borrow a capability for the NFTMinter in storage self.minterRef = acct.borrow<&NonFungibleTicket.NFTMinter>(from: /storage/NFTMinter) ?? panic("Could not borrow minter reference") }
execute { // Use the minter reference to mint an NFT, which deposits // the NFT into the collection that is sent as a parameter. let newNFT <- self.minterRef.mintNFT()
let metadata : {String : String} = { "event": "FLOW LIVE in Concert!", "section": "200", "row": "3", "seat": "1", "uri": "https://flow-ticket-exchange.s3.amazonaws.com/ticket.png" }
self.receiverRef.deposit(token: <-newNFT, metadata: metadata)
log("Ticket minted and deposited into admin account") } }
|
對我而言,這部分有趣的環節是NFT的元數據。我創建了一個演示用的具有若干屬性的票證, 例如區域和排,以及一個指向票證圖像的URI鏈接。
這引起了我的思考,我不知道是否輕量級NFT用戶理解其工作原理。
區塊鏈在跟蹤NFT的持有人以及其相關的元數據方面表現出色。然而,數字資產更常見的實現 方式是采用外部存儲來保存這些資產的實際內容。
作為S3 bucket服務的用戶,沒有什么可以阻止我刪除或更新這些文件!
想象一下,你花費3萬美元購買了Steph Curry的3分球,然而Dapper Lab悄悄地將其替換為 Alex Caruso的罰球!希望像IIPFS這樣的去中心化存儲方案能夠解決這一類問題。
我們的票務發行賬號部署合約,因此該賬號在其私有存儲中保存了NFTMinter資源。必須使用 這個賬號來簽名如下交易:
1
|
flow transactions ./cadence/transactions/MintTicket.cdc send --signer emulator-account
|
如果我們嘗試用參與者賬號簽名,交易就會失敗。接下來讓我們用一個Cadence腳本來檢查我們 的票務簽發賬號的余額。
1 2 3 4 5 6 7 8 9 10 11
|
import NonFungibleTicket from 0xf8d6e0586b0a20c7
pub fun main() : [UInt64] { let acct1 = getAccount(0xf8d6e0586b0a20c7) let capability1 = acct1.getCapability<&{NonFungibleTicket.NFTReceiver}>(/public/NFTReceiver)
let receiverRef1 = capability1.borrow() ?? panic("Could not borrow the receiver reference")
return receiverRef1.getIDs() }
|
使用如下命令運行腳本:
1
|
flow scripts execute ./cadence/scripts/CheckTicketBalance.cdc
|
然后你將看到一個包含所持有NFT的ID的數組:
這表示發行賬號目前持有我們新鑄造的NFT!
5、使用Cadence腳本轉讓門票NFT
現在我們將把門票轉讓給一位熱切等待的樂迷。首先我們將在參與者的存儲中 創建一個NFTCollection資源。
這讓我們有機會了解FLow架構的一個有用的方面。
在以太坊中,如果你將以太幣發送到一個無效的錢包地址,這些以太幣就沒了。 然而在FLow中,在沒有明確的目標地址時資源不可能發送出去,或者整個交易回滾。 我們不會因為手誤發送到無效地址而失去門票。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
import NonFungibleTicket from 0xf8d6e0586b0a20c7
// This transaction configures a user's account // to use the NFT contract by creating a new empty collection, // storing it in their account storage, and publishing a capability transaction { prepare(acct: AuthAccount) {
// Create a new empty collection let collection <- NonFungibleTicket.createEmptyCollection()
// store the empty NFT Collection in account storage acct.save<@NonFungibleTicket.Collection>(<-collection, to: /storage/NFTCollection)
log("Collection created for account 2")
// create a public capability for the Collection acct.link<&{NonFungibleTicket.NFTReceiver}>(/public/NFTReceiver, target: /storage/NFTCollection)
log("Capability created") } }
|
使用以上交易運行如下命令:
1
|
flow transactions send .\cadence\transactions\SetupEmptyCollection.cdc --signer attendee-account
|
現在我們的參與者已經准備接收票據了。我們將使用Cadence交易來完成這個操作,在這個交易中 發行賬號取出其NFT然后存入參與者的藏品存儲。
別忘了每次存入時,合約都會增加存儲在NFT中的numTransfers參數的值。在這個交易之后,numTransfers = 1. 下面是合約的內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
import NonFungibleTicket from 0xf8d6e0586b0a20c7
// This transaction transfers an NFT from one user's collection // to another user's collection. transaction {
// The field that will hold the NFT as it is being // transferred to the other account let transferToken: @NonFungibleTicket.NFT let metadata: { String : String }
prepare(acct: AuthAccount) {
// Borrow a reference from the stored collection let collectionRef = acct.borrow<&NonFungibleTicket.Collection>(from: /storage/NFTCollection) ?? panic("Could not borrow a reference to the owner's collection") self.metadata = collectionRef.getMetadata(id: 1) // Call the withdraw function on the sender's Collection // to move the NFT out of the collection self.transferToken <- collectionRef.withdraw(withdrawID: 1) }
execute { // Get the recipient's public account object let recipient = getAccount(0x01cf0e2f2f715450)
// Get the Collection reference for the receiver // getting the public capability and borrowing a reference from it let receiverRef = recipient.getCapability<&{NonFungibleTicket.NFTReceiver}>(/public/NFTReceiver) .borrow() ?? panic("Could not borrow receiver reference") // Deposit the NFT in the receivers collection receiverRef.deposit(token: <-self.transferToken, metadata: self.metadata)
log("NFT ID 1 transferred from account 2 to account 1") } }
|
使用下面的命令轉讓門票:
1
|
flow transactions send ./cadence/transactions/TransferTicket.cdc --signer emulator-account
|
你可以分別用兩個賬號運行CheckTicketBalance腳本,驗證下getIDs()在使用發行賬號時 返回空數組,而在使用參與者賬號時返回[1]
!接下來讓我們看看如果試圖將門票轉回 發行賬號會發生什么情況。
1 2 3 4 5 6 7
|
❌ Transaction Error execution error code 100: Execution failed: error: panic: Ticket is non-transferrable! --> f8d6e0586b0a20c7.NonFungibleTicket:59:16 | 59 | panic("Ticket is non-transferrable!") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
我們的智能合約正確地杜絕了這種情況的發生。我們的門票在二級市場不可以再次銷售!
6、門票NFT應用的React前端實現
我們不會深入介紹前端應用的細節,它主要是利用FLow JS庫訪問我們的Cadence合約。 在這個簡單的示例程序中,我們讀取NFT的元數據,但是你可以用同樣的方法執行任何Cadence代碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
|
import React, { useState, useEffect } from "react"; import * as fcl from "@onflow/fcl"; import { Address } from "@onflow/types"; import * as t from "@onflow/types"
const TokenData = () => { const [user, setUser] = useState({loggedIn: null}) useEffect(() => fcl.currentUser().subscribe(setUser), []) const [nftInfo, setNftInfo] = useState(null) const fetchTokenData = async () => { const encoded = await fcl .send([ fcl.script` import NonFungibleTicket from 0xf8d6e0586b0a20c7 pub fun main() : {String : String} { let nftOwner = getAccount(0xf8d6e0586b0a20c7) let capability = nftOwner.getCapability<&{NonFungibleTicket.NFTReceiver}>(/public/NFTReceiver) let receiverRef = capability.borrow() ?? panic("Could not borrow the receiver reference") return receiverRef.getMetadata(id: 1) } ` ]) const decoded = await fcl.decode(encoded) setNftInfo(decoded) }; return ( <div className="listing"> <div className="center"> <button className="btn-primary" onClick={fetchTokenData}>Fetch Token Data</button> </div> { nftInfo && <div> <div className="center"> <p>Event: {nftInfo["event"]}</p> <p>Section: {nftInfo["section"]}</p> <p>Row: {nftInfo["row"]}</p> <p>Seat: {nftInfo["seat"]}</p> </div> <div className="center image"> <img src={nftInfo["uri"]} alt="My NFT!" width="90%"/> <div> <button onClick={() => setNftInfo(null)} className="btn-secondary">Clear Token Info</button> </div> </div> </div> } </div> ); };
const OwnerData = (account) => { const [ownerInfo, setOwnerInfo] = useState(null) const fetchOwnerData = async () => { const encoded = await fcl .send([ fcl.args( fcl.arg(account, t.Address) ), fcl.script` import NonFungibleTicket from 0xf8d6e0586b0a20c7 pub fun main() : [UInt64] { let acct1 = getAccount(account) let capability1 = acct1.getCapability<&{NonFungibleTicket.NFTReceiver}>(/public/NFTReceiver) let receiverRef1 = capability1.borrow() ?? panic("Could not borrow the receiver reference") return receiverRef1.getIDs() } ` ]) const decoded = await fcl.decode(encoded) setOwnerInfo(decoded) }; return ( <div> <p>Account: {account}</p> <p>Owned NFT's: {ownerInfo}</p> </div> ); }; export default TokenData;
|
使用如下命令啟動我們的前端應用:
下面就是我們的簡單但強大的去中心化門票查看網頁:
完整的代碼可以從這里下載。
https://gitee.com/cexchange/CoinExchange?_from=gitee_search#http://118.25.133.182:84/#/
特色
特色1: 基於內存撮合引擎,與傳統基於數據庫撮合更快
特色2: 前后端分離,基於Token的Api授權機制
特色3: 基於SpringCloud微服務架構,擴展更容易
特色4: MySQL、MongoDB、Redis多種數據存儲方式,只為更快
特色5: Kafka發布訂閱消息隊列,讓訂單更快流轉
特色6: 主流幣種對接區塊鏈接口齊全,開箱即用
特色7: 冷熱錢包分離,兩種提現方式,保證安全
特色8: 機器人系統,同步行情,維護深度,防止搬磚
特色9: 原生App,Java和ObjectC提供原生體驗
特色10: 交易所設計者提供技術支持,部署+二開無憂
特色11: 支持添加自定義平台幣及其他幣種
聲明一:我已在新公司上班,一些說明性的東西我會抽空在這里更新,以方便大家編譯、搭建、開發
聲明二:APP源碼及交易機器人源碼未開源,有需要的添加QQ:877070886
聲明四:請不要用本開源代碼直接搭建交易所!本源碼尚有一些隱藏BUG,僅供學習!否則后果自負!
聲明五:本交易所完整源碼僅向有技術團隊或技術實力的人提供,小白或不同技術的請勿咨詢!
新功能持續開發中,敬請期待
- 代理商系統(100%,5月已完成,未開源)
- 極速兌換(100%,6月已完成,未開源)
- IEO首發活動(100%,6月已完成,未開源)
- 永續合約(100%,8月完成,不開源)
- 期權合約(100%,9月完成,不開源)
- 秒合約(0%,11月完成,不開源)
- 指數合約(0%,12月完成,不開源)
- 差價合約(0%,1月完成,不開源)
- 交割合約(0%,2月完成,不開源)
- 智能客服系統(0%,1月完成,不開源)
系統架構概要
隨便畫的幾個草圖,湊合看吧。。。
整體

邏輯架構

部署架構

依賴關系

關於SpringCloud
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的開發便利性巧妙地簡化了分布式系統基礎設施的開發,如服務發現注冊、配置中心、消息總線、負載均衡、斷路器、數據監控等,都可以用Spring Boot的開發風格做到一鍵啟動和部署。Spring Cloud並沒有重復制造輪子,它只是將各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,通過Spring Boot風格進行再封裝屏蔽掉了復雜的配置和實現原理,最終給開發者留出了一套簡單易懂、易部署和易維護的分布式系統開發工具包。 一般而言,一個完整的SpringCloud框架應該如下圖所示:

關於撮合交易引擎
本系統對交易隊列采用內存撮合的方式進行,以Kafka做撮合訂單信息傳輸,MongoDB持久化訂單成交明細,MySQL記錄訂單總體成交。 其中01_Framework/Exchange項目主要負責內存撮合,01_Framework/Market項目主要負責訂單成交持久化、行情生成、行情推送等服務,包括:
- K線數據,間隔分別為:1分鍾、5分鍾、15分鍾、30分鍾、1小時、1天、1周、1月
- 所有交易對的市場深度(market depth)數據
- 所有交易對的最新價格
- 最近成交的交易對
內存撮合交易支持的模式
- 限價訂單與限價訂單撮合
- 市價訂單與限價訂單撮合
- 限價訂單與市價訂單撮合
- 市價訂單與市價訂單撮合
限價&市價訂單處理邏輯

撮合引擎支持的其他功能
除了普通的限價與市價撮合交易功能外,本系統的撮合交易引擎還引入了活動成交模式,通過設置交易對(如:BTC/USDT)的開始交易時間、初始發行量、初始發行價、活動模式等參數,可以制定出豐富的撮合交易模式,從而滿足不同的撮合模式。
舉例說明
交易所預計在2020年8月8日 12時00分00秒上線交易對AAA/USDT,但是作為一個新上線的幣種,沒有活動怎么能行呢?項目方或交易所決定拿出10000個AAA以0.0001USDT(市場行情價:0.0005)的價格讓大家搶購。本系統支持對這種活動的設置。
另外,如果項目方或交易所決定拿出10000個AAA以0.0001USDT的價格發行,不希望大家搶購,而是希望所有充值USDT的用戶能夠平均瓜分10000個AAA,本系統也支持這種活動的設置。
總結
總之,本系統支持高度自定義的撮合模式,同時你也可以開發出你自己想要的撮合交易模式,只需要通過修改Exchange項目中的撮合邏輯就可以。
關於技術構成
- 后端:Spring、SpringMVC、SpringData、SpringCloud、SpringBoot
- 數據庫:Mysql、Mongodb
- 其他:redis、kafka、阿里雲OSS、騰訊防水校驗
- 前端:Vue、iView、less
這個是給客戶做的,但是后來客戶不運營了,所以遺留了這個網站,因為我沒有服務器權限,所以這個網站隨時可能無法訪問。
搭建起來一個測試站點需要購買好幾台雲服務器,成本較大,所以我自己沒有搭建測試站,但是系統是完整的,經過了將近一年的商用及實際運營考驗。
關於交易機器人
交易機器人是自動交易的程序,可以根據外部行情,自動進行交易,讓本交易所的交易對價格與外部保持一致,防止因部分用戶“搬磚”導致的損失。
關於搬磚
例如A交易所BTC價格是10000USDT,而B交易所的BTC價格是9500USDT,搬磚就是在B交易所通過9500USDT的價格買入BTC,然后轉賬到A交易所,賺取差價(500USDT)。
如果交易所沒有交易機器人,就會導致本交易所的幣種價格與其他主流交易所相比有差價,從而讓用戶“搬磚”,導致交易所損失。
另外,交易機器人還有一個功能,就是在交易所初期運營的時候,形成一個初期的交易深度,不會讓用戶覺得交易所冷清,沒有用戶。
我本人是Java程序員,對移動端開發不太了解,所以包調試安裝實際上也是我付費請別人幫忙的。
如果你沒有技術人員,我可以幫助你搭建一套完整的交易所系統,但是需要你請一到兩名維護人員,因為系統的穩定運行少不了運維人員。
聯系QQ:877070886
==============================================
系統運行環境
- Centos 6.8
- MySQL 5.5.16
- Redis-x64-3.2.100
- Mongodb 3.6.13
- kafka_2.11-2.2.1
- nginx-1.16.0
- JRE 8u241
- JDK 1.8
- Vue
- Zookeeper
生產環境建議配置

文件目錄說明
00_framework
└─———admin 后台管理API
└─———bitrade-job 任務管理
└─———chat OTC聊天
└─———cloud SpringCloud微服務管理
└─———core 核心
└─———exchange 撮合交易引擎
└─———exchange-api 撮合交易API
└─———exchange-core 撮合交易核心
└─———jar 第三方類庫
└─———market 市場行情API、K線生成
└─———otc-api OTC交易API(如無需otc功能可不啟動)
└─———otc-core OTC核心
└─———sql SQL腳本
└─———ucenter-api 用戶個人中心API
└─———wallet 錢包資產管理,負責與RPC對接
01_wallet_rpc
└─———bitcoin
└─———bsv
└─———btm
└─———eos
└─———erc-eusdt
└─———erc-token(可對接各種ERC20幣種)
└─———eth
└─———ltc
└─———usdt
02_App_Android
03_App_IOS
04_Web_Admin
05_Web_Front
使用教程
- 准備mysql數據庫,創建名稱為“xxxx”的數據庫
- 准備redis緩存數據庫
- 准備kafka流式處理環境(先配置運行zookper,接着配置運行kafka)
- 准備mongodb數據庫環境,創建用戶admin、xxxx,創建bitrade數據庫
- 准備阿里雲OSS(修改項目中需要配置的地方)
- 准備nginx,修改配置文件(可選,正式上線需配置)
- 修改framework代碼中的配置文件為准備環境配置參數
- 編譯生成jar可執行文件
- 運行cloud.jar(微服務注冊中心)
- 運行exchange.jar(撮合交易引擎)
- 運行market.jar(行情中心,需要等待Exchange.jar完全啟動)
- 運行ucenter.jar(用戶中心)
- 運行其他模塊(wallet.jar、chat.jar、otc-api.jar等)
- 打開mysql,導入framework代碼中的sql文件夾中xxxxxxx.sql文件,注意,trigger的sql如果報錯,需要針對wallet表添加trigger
- 運行前端vue項目
- 運行后端vue項目
- 運行錢包RPC
- 運行自動交易機器人程序(本部分代碼未上傳,但不影響)
- 運行Admin項目(該服務並不依賴其他服務,因此也可只運行此項目,直接查看后台)
技術支持
本數字貨幣交易系統系我所在公司為交易所開發的項目,該交易所因團隊原因已停止運營,我司也已於2月解散。因我參與項目時,負責整體研發管理、架構設計以及客戶對接,所以掌握所有代碼。
本系統在功能使用上有一些需要特別注意的地方,例如新建交易對以后的其他操作,操作不當會引起數據紊亂的錯誤出現。
本人可提供有償技術幫助與使用培訓指導!