Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
dex-admin
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
haojie
dex-admin
Commits
b05f894e
Commit
b05f894e
authored
Jun 30, 2023
by
haojie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1
parent
73b3f4c4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
717 additions
and
83 deletions
+717
-83
app/Exceptions/AppRuntimeException.php
+0
-1
app/Exceptions/Handler.php
+36
-0
app/Http/Controllers/Api/InscriptionController.php
+2
-2
app/Http/Controllers/Api/TradeController.php
+47
-1
app/Http/Controllers/Web/TradeController.php
+6
-0
app/Models/InscriptionTrading.php
+8
-7
app/Models/TransactionLog.php
+18
-0
app/Service/Common/CommonService.php
+21
-1
app/Service/TradeService.php
+174
-16
app/Service/TransactionLogService.php
+42
-0
database/migrations/2023_06_29_065643_create_inscription_trading_table.php
+6
-12
database/migrations/2023_06_29_085620_create_transaction_log_table.php
+5
-1
resources/js/Pages/InscriptionShelves/components/MyInscription.vue
+0
-0
resources/js/Pages/InscriptionShelves/components/SellDialog.vue
+68
-5
resources/js/Pages/InscriptionShelves/components/confirmDialog.vue
+102
-0
resources/js/Pages/Trade/components/BuyDialog.vue
+62
-7
resources/js/Pages/Trade/components/Market.vue
+27
-22
resources/js/Pages/Trade/index.vue
+5
-3
resources/js/components/card.vue
+2
-0
resources/js/utils/api/index.ts
+23
-0
resources/js/utils/api/public.ts
+46
-1
resources/js/utils/ethers.ts
+9
-4
routes/api.php
+8
-0
No files found.
app/Exceptions/AppRuntimeException.php
View file @
b05f894e
...
...
@@ -4,7 +4,6 @@
use
Illuminate\Http\Request
;
use
Illuminate\Support\Str
;
use
RuntimeException
;
use
Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
;
...
...
app/Exceptions/Handler.php
View file @
b05f894e
...
...
@@ -2,6 +2,7 @@
namespace
App\Exceptions
;
use
Illuminate\Auth\AuthenticationException
;
use
Illuminate\Foundation\Exceptions\Handler
as
ExceptionHandler
;
use
Throwable
;
...
...
@@ -37,6 +38,27 @@ class Handler extends ExceptionHandler
];
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public
function
render
(
$request
,
Throwable
$exception
)
{
if
(
$exception
instanceof
AppRuntimeException
)
{
return
$exception
::
render
(
$request
,
$exception
);
}
if
(
$exception
instanceof
\Illuminate\Auth\AuthenticationException
)
{
$exception
=
new
\App\Exceptions\UserException
(
0
,
'TOKEN失效了'
,
[],
403
);
return
AppRuntimeException
::
render
(
$request
,
$exception
);
}
return
parent
::
render
(
$request
,
$exception
);
}
/**
* Register the exception handling callbacks for the application.
*
* @return void
...
...
@@ -47,4 +69,18 @@ public function register()
//
});
}
/**
* Convert an authentication exception into a response.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Auth\AuthenticationException $exception
* @return \Symfony\Component\HttpFoundation\Response
*/
protected
function
unauthenticated
(
$request
,
AuthenticationException
$exception
)
{
return
$request
->
expectsJson
()
?
response
()
->
json
([
'message'
=>
$exception
->
getMessage
()],
401
)
:
redirect
()
->
guest
(
$exception
->
redirectTo
()
??
route
(
'login'
));
}
}
app/Http/Controllers/Api/InscriptionController.php
View file @
b05f894e
...
...
@@ -14,7 +14,7 @@ public function wallet(Request $request)
{
$address
=
$request
->
input
(
'address'
);
$result
=
app
(
InscriptionService
::
class
)
->
getWalletInscription
(
$address
);
return
$this
->
success
(
$result
);
return
$this
->
success
(
'success'
,
$result
);
}
// 获取外部网站的市场铭文列表
...
...
@@ -23,7 +23,7 @@ public function outMarket(Request $request)
$tick
=
$request
->
input
(
'tick'
);
$type
=
$request
->
input
(
'type'
);
$result
=
app
(
InscriptionService
::
class
)
->
getMarketInscription
();
return
$this
->
success
(
$result
);
return
$this
->
success
(
'success'
,
$result
);
}
}
app/Http/Controllers/Api/TradeController.php
View file @
b05f894e
...
...
@@ -21,7 +21,53 @@ public function shelves(Request $request)
// 铭文信息
$inscription
=
$request
->
input
(
'inscription'
);
$result
=
app
(
TradeService
::
class
)
->
userShelves
(
$payment_address
,
$hash
,
$inscription
,
$price
);
return
$this
->
success
(
$result
);
return
$this
->
success
(
'success'
,
$result
);
}
// 交易检测回调
public
function
check
(
Request
$request
)
{
$data
=
$request
->
input
();
$result
=
app
(
TradeService
::
class
)
->
check
(
$data
);
return
$this
->
success
(
'success'
,
$result
);
}
// 铭文查询
public
function
search
(
Request
$request
)
{
$address
=
$request
->
input
(
'address'
);
$type
=
$request
->
input
(
'type'
);
$page
=
$request
->
input
(
'page'
,
1
);
$limit
=
$request
->
input
(
'limit'
,
10
);
// 限制最大
if
(
$limit
>
50
)
{
$limit
=
10
;
}
$result
=
app
(
TradeService
::
class
)
->
search
(
$address
,
$type
,
$page
,
$limit
);
return
$this
->
success
(
'success'
,
$result
);
}
// 获取平台交易中的铭文列表
public
function
inscriptionList
(
Request
$request
)
{
$page
=
$request
->
input
(
'page'
,
1
);
$limit
=
$request
->
input
(
'limit'
,
10
);
// 限制最大
if
(
$limit
>
50
)
{
$limit
=
10
;
}
$result
=
app
(
TradeService
::
class
)
->
inscriptionList
(
$page
,
$limit
);
return
$this
->
success
(
'success'
,
$result
);
}
// 买家支付
public
function
buy
(
Request
$request
)
{
$payment_address
=
$request
->
input
(
'payment_address'
,
''
);
$hash
=
$request
->
input
(
'hash'
,
''
);
$id
=
$request
->
input
(
'id'
,
''
);
$result
=
app
(
TradeService
::
class
)
->
buy
(
$payment_address
,
$hash
,
$id
);
return
$this
->
success
(
'success'
,
$result
);
}
}
app/Http/Controllers/Web/TradeController.php
View file @
b05f894e
...
...
@@ -13,9 +13,15 @@ public function index()
{
// 铭文交易页面
$title
=
'铭文交易'
;
// 获取买入手续费
$buy_fee
=
app
(
AdminConfigService
::
class
)
->
getBuyFee
();
// 收款账号
$receipt_account
=
app
(
AdminConfigService
::
class
)
->
getReceiptAccount
();
return
Inertia
::
render
(
'Trade/index'
,
[
'info'
=>
[
'title'
=>
$title
,
'buy_fee'
=>
$buy_fee
,
'receipt_account'
=>
$receipt_account
],
]);
}
...
...
app/Models/InscriptionTrading.php
View file @
b05f894e
...
...
@@ -12,12 +12,7 @@ class InscriptionTrading extends Model
protected
$table
=
'inscription_trading'
;
protected
$fillable
=
[
'content_uri'
,
'quantity'
,
'owner'
,
'creator'
,
'inscription_created_date'
,
'inscription_hash'
,
'inscription'
,
'original_amount'
,
'admin_account'
,
'sell_fee'
,
...
...
@@ -31,6 +26,12 @@ class InscriptionTrading extends Model
'seller_receive_hash'
,
'seller_cancel_hash'
,
'buyer_pay_hash'
,
'buyer_receive_inscription_hash'
'buyer_receive_inscription_hash'
,
'seller_address'
];
// 数组
protected
$casts
=
[
'inscription'
=>
'array'
,
];
}
app/Models/TransactionLog.php
View file @
b05f894e
...
...
@@ -10,4 +10,22 @@ class TransactionLog extends Model
use
HasFactory
;
protected
$table
=
'transaction_log'
;
// 可以修改的字段
protected
$fillable
=
[
'to'
,
'from'
,
'hash'
,
'order_id'
,
'status'
,
'type'
,
'title'
,
'amount'
,
'error_message'
];
// 数组
protected
$casts
=
[
'error_message'
=>
'array'
,
];
}
app/Service/Common/CommonService.php
View file @
b05f894e
...
...
@@ -5,6 +5,7 @@
use
App\Exceptions\UserException
;
use
Illuminate\Support\Facades\Log
;
class
CommonService
{
...
...
@@ -14,10 +15,29 @@ class CommonService
// 自定义字段校验
public
function
customValidate
(
$data
,
$fields
,
$messages
=
'校验未通过'
,
$code_type
=
1
)
{
$params
=
[];
foreach
(
$fields
as
$field
)
{
if
(
blank
(
$data
[
$field
]))
{
if
(
array_key_exists
(
$field
,
$data
)
&&
filled
(
$data
[
$field
]))
{
// 添加数据
$params
[
$field
]
=
$data
[
$field
];
}
else
{
throw
new
UserException
(
$code_type
,
$messages
);
}
}
return
$params
;
}
// 获取交易检测回调地址
public
function
getTradeCheckCallbackUrl
()
{
$notify_url
=
''
;
// 判断是否本地环境
if
(
app
()
->
environment
(
'local'
))
{
$notify_url
=
'http://192.168.1.5:8080/api/inscription/admin/check'
;
}
else
{
// 当前域名
$notify_url
=
config
(
'app.url'
)
.
'/api/inscription/admin/check'
;
}
return
$notify_url
;
}
}
app/Service/TradeService.php
View file @
b05f894e
...
...
@@ -5,6 +5,8 @@
use
App\Exceptions\UserException
;
use
App\Models\InscriptionTrading
;
use
App\Service\Common\CommonService
;
use
Illuminate\Support\Facades\Log
;
use
Illuminate\Support\Facades\Redis
;
class
TradeService
{
...
...
@@ -33,6 +35,38 @@ class TradeService
public
const
BUYER_RECEIVE_FAIL
=
5
;
// 买家接收铭文失败(平台转移失败)
public
const
BUYER_RECEIVE_SUCCESS
=
6
;
// 买家接收铭文成功
// 交易类型
public
const
TRADE_TYPE_INSCRIPTION
=
1
;
// 铭文转移
public
const
TRADE_TYPE_PAY
=
2
;
// 支付
// 日志的交易状态
public
const
TRADE_STATUS_WAIT
=
1
;
// 未开始
public
const
TRADE_STATUS_SUCCESS
=
2
;
// 已完成
public
const
TRADE_STATUS_FAIL
=
3
;
// 失败
public
const
TRADE_STATUS_FAIL_2
=
4
;
// 支付成功,接收地址错误
public
const
TRADE_STATUS_ERROR
=
5
;
// 检测失败
// 交易名称(具体类型)
public
const
TRADE_NAME_SELLER_INSCRIPTION
=
1
;
// 卖家铭文转移
public
const
TRADE_NAME_SELLER_CANCEL
=
2
;
// 卖家取消(下架)
public
const
TRADE_NAME_SELLER_RECEIPT
=
3
;
// 卖家收款
public
const
TRADE_NAME_BUYER_PAY
=
4
;
// 买家支付
public
const
TRADE_NAME_BUYER_RECEIVE
=
5
;
// 买家接收铭文
public
const
TRADE_NAME_LIST
=
[
self
::
TRADE_NAME_SELLER_INSCRIPTION
=>
'卖家铭文转移'
,
self
::
TRADE_NAME_SELLER_CANCEL
=>
'卖家取消(下架)'
,
self
::
TRADE_NAME_SELLER_RECEIPT
=>
'卖家收款'
,
self
::
TRADE_NAME_BUYER_PAY
=>
'买家支付'
,
self
::
TRADE_NAME_BUYER_RECEIVE
=>
'买家接收铭文'
,
];
// redis key
public
const
REDIS_KEY_RECEIPT
=
'admin_check_receipt'
;
// 到账查询
// 查询
public
function
model
()
{
return
new
InscriptionTrading
();
...
...
@@ -44,44 +78,168 @@ public function create($data)
return
$this
->
model
()
->
create
(
$data
);
}
public
function
redisAdd
(
$data
,
$key
=
self
::
REDIS_KEY_RECEIPT
)
{
Redis
::
rpush
(
$key
,
json_encode
(
$data
));
}
// 更新铭文数据
public
function
updateInscription
(
$data
)
{
// 铭文信息必须包含指定字段
$fields
=
[
'transaction_hash'
,
'current_owner'
,
'content_uri'
,
'creator'
,
'creation_timestamp'
];
// 校验铭文信息
$inscription
=
app
(
CommonService
::
class
)
->
customValidate
(
$data
,
$fields
,
'缺少必要的铭文信息'
);
return
$inscription
;
}
// 铭文列表
public
function
inscriptionList
(
$page
,
$limit
)
{
// 指定返回的字段
$field
=
[
'id'
,
'inscription'
,
'status'
,
'original_amount'
];
$model
=
$this
->
model
()
->
where
(
'status'
,
self
::
ORDER_STATUS_ON
)
->
where
(
'buyer_address'
,
null
)
->
orderBy
(
'id'
,
'desc'
)
->
paginate
(
$limit
,
$field
,
'page'
,
$page
);
// 分页
return
$model
;
}
public
function
userShelves
(
$payment_address
,
$hash
,
$inscription
,
$price
)
{
if
(
blank
(
$payment_address
)
||
blank
(
$hash
)
||
blank
(
$inscription
))
{
throw
new
UserException
(
1
,
'
禁止访问
'
);
throw
new
UserException
(
1
,
'
非法操作
'
);
}
// 铭文信息必须包含指定字段
$fields
=
[
'content_uri'
,
'quantity'
,
'owner'
,
'creator'
,
'inscription_created_date'
,
'inscription_hash'
];
// 校验铭文信息
app
(
CommonService
::
class
)
->
customValidate
(
$inscription
,
$fields
,
'缺少必要的铭文信息'
);
$inscription
=
self
::
updateInscription
(
$inscription
);
// 重复的hash禁止上架
$is_exist
=
$this
->
model
()
->
where
(
'hash'
,
$hash
)
->
first
();
$is_exist
=
$this
->
model
()
->
where
(
'
seller_transfer_inscription_
hash'
,
$hash
)
->
first
();
if
(
$is_exist
)
{
throw
new
UserException
(
1
,
'
禁止访问
'
);
throw
new
UserException
(
1
,
'
非法操作
'
);
}
// 卖家手续费比例
$sell_fee
=
app
(
AdminConfigService
::
class
)
->
getSellFee
();
// 手续费具体金额
$sell_fee_amount
=
$price
*
$sell_fee
;
// 平台收款账号
$admin_address
=
app
(
AdminConfigService
::
class
)
->
getReceiptAccount
();
// 创建
$data
=
[
'content_uri'
=>
$inscription
[
'content_uri'
],
'quantity'
=>
$inscription
[
'quantity'
],
'owner'
=>
$inscription
[
'owner'
],
'creator'
=>
$inscription
[
'creator'
],
'inscription_created_date'
=>
$inscription
[
'inscription_created_date'
],
'inscription_hash'
=>
$inscription
[
'inscription_hash'
],
'inscription'
=>
$inscription
,
'original_amount'
=>
$price
,
// 收款账号
'admin_account'
=>
app
(
AdminConfigService
::
class
)
->
getReceiptAccount
()
,
'admin_account'
=>
$admin_address
,
'sell_fee'
=>
$sell_fee
,
'sell_fee_amount'
=>
$sell_fee_amount
,
'status'
=>
self
::
ORDER_STATUS_WAIT
,
'seller_status'
=>
self
::
SELLER_STATUS_
WAIT
,
'seller_status'
=>
self
::
SELLER_STATUS_
PROGRESS
,
'buyer_status'
=>
self
::
BUYER_STATUS_WAIT
,
'seller_transfer_inscription_hash'
=>
$hash
,
// 卖家地址
'seller_address'
=>
$payment_address
,
];
$status
=
$this
->
create
(
$data
);
$result
=
$this
->
create
(
$data
);
if
(
$result
)
{
// 创建日志
$data
[
'order_id'
]
=
$result
->
id
;
$data
[
'trade_type'
]
=
self
::
TRADE_TYPE_INSCRIPTION
;
$data
[
'title'
]
=
self
::
TRADE_NAME_SELLER_INSCRIPTION
;
$data
[
'to'
]
=
$admin_address
;
$data
[
'from'
]
=
$payment_address
;
$data
[
'hash'
]
=
$hash
;
app
(
TransactionLogService
::
class
)
->
create
(
$data
);
// 回调地址
$notify_url
=
app
(
CommonService
::
class
)
->
getTradeCheckCallbackUrl
();
// 铭文转移查铭文数据里的hash就可以了
$obj
=
[
'from'
=>
$payment_address
,
'to'
=>
$admin_address
,
'hash'
=>
$hash
,
'inscription_hash'
=>
$inscription
[
'transaction_hash'
],
'id'
=>
$result
->
id
,
'type'
=>
self
::
TRADE_TYPE_INSCRIPTION
,
// log标题
'title'
=>
self
::
TRADE_NAME_SELLER_INSCRIPTION
,
'notify_url'
=>
$notify_url
];
// 添加reids队列
self
::
redisAdd
(
$obj
);
return
true
;
}
throw
new
UserException
(
1
,
'上架失败'
);
}
// 卖家上架回调更新
public
function
sellerShelvesSuccess
(
$data
)
{
$status
=
$data
[
'status'
];
// 商品默认状态
$order_status
=
self
::
ORDER_STATUS_WAIT
;
// 卖家状态
$seller_status
=
self
::
SELLER_STATUS_PROGRESS
;
if
(
$status
==
self
::
TRADE_STATUS_SUCCESS
)
{
// 改为已上架
$order_status
=
self
::
ORDER_STATUS_ON
;
// 卖家转移成功
$seller_status
=
self
::
SELLER_STATUS_SUCCESS
;
}
elseif
(
$status
==
self
::
TRADE_STATUS_FAIL
||
$status
==
self
::
TRADE_STATUS_FAIL_2
)
{
// 支付失败
$seller_status
=
self
::
SELLER_STATUS_FAIL
;
}
$update
=
[
'status'
=>
$order_status
,
'seller_status'
=>
$seller_status
];
$inscription
=
$data
[
'inscription'
]
??
null
;
// 更新铭文信息
if
(
$inscription
)
{
$inscription
=
self
::
updateInscription
(
$data
[
'inscription'
]);
$update
[
'inscription'
]
=
$inscription
;
}
$result
=
$this
->
model
()
->
where
(
'id'
,
$data
[
'id'
])
->
update
(
$update
);
if
(
$result
)
{
return
true
;
}
throw
new
UserException
(
1
,
'操作失败'
);
}
// 买家支付修改状态
public
function
buyerPaymentStatus
()
{
}
// 到账检测回调
public
function
check
(
$data
)
{
// 更新日志
app
(
TransactionLogService
::
class
)
->
update
(
$data
);
// 根据日志标题更新市场列表
$title_type
=
$data
[
'title'
];
if
(
$title_type
==
self
::
TRADE_NAME_SELLER_INSCRIPTION
)
{
// 卖家铭文转移
self
::
sellerShelvesSuccess
(
$data
);
}
else
if
(
$title_type
==
self
::
TRADE_NAME_BUYER_PAY
)
{
// 买家支付
self
::
buyerPaymentStatus
();
}
return
true
;
}
// 铭文交易记录
public
function
search
(
$address
,
$type
,
$page
,
$limit
)
{
if
(
blank
(
$address
)
||
blank
(
$type
))
{
throw
new
UserException
(
1
,
'非法操作'
);
}
$model
=
$this
->
model
()
->
where
(
'status'
,
$type
)
->
where
(
'seller_address'
,
$address
)
->
orderBy
(
'id'
,
'desc'
)
->
paginate
(
$limit
,
[
'inscription'
,
'status'
,
'original_amount'
,
'sell_fee'
,
'seller_receive_hash'
],
'page'
,
$page
);
// 分页
return
$model
;
}
// 买家支付
public
function
buy
(
$payment_address
,
$hash
,
$id
)
{
if
(
blank
(
$payment_address
)
||
blank
(
$hash
)
||
blank
(
$id
))
{
throw
new
UserException
(
1
,
'非法操作'
);
}
// 加字段,卖家卖出时收款地址,买家 购买时收款地址
}
}
app/Service/TransactionLogService.php
0 → 100644
View file @
b05f894e
<?php
namespace
App\Service
;
use
App\Models\TransactionLog
;
use
Illuminate\Support\Facades\Log
;
class
TransactionLogService
{
// 新增日志
public
function
create
(
$params
)
{
$data
=
[];
$data
[
'to'
]
=
$params
[
'to'
];
$data
[
'from'
]
=
$params
[
'from'
];
$data
[
'hash'
]
=
$params
[
'hash'
];
$data
[
'order_id'
]
=
$params
[
'order_id'
];
// 状态
$data
[
'status'
]
=
TradeService
::
TRADE_STATUS_WAIT
;
$data
[
'type'
]
=
$params
[
'trade_type'
];
$data
[
'title'
]
=
$params
[
'title'
];
// amount
// error_message
TransactionLog
::
query
()
->
create
(
$data
);
}
public
function
update
(
$data
)
{
// callback status 1:失败 2:成功 3:支付成功,接收地址错误
$model
=
TransactionLog
::
query
()
->
where
(
'order_id'
,
$data
[
'id'
])
->
where
(
'type'
,
$data
[
'type'
])
->
where
(
'to'
,
$data
[
'to'
])
->
where
(
'from'
,
$data
[
'from'
])
->
where
(
'hash'
,
$data
[
'hash'
]);
if
(
$data
[
'type'
]
==
TradeService
::
TRADE_TYPE_PAY
)
{
// 添加金额
$model
->
where
(
'amount'
,
$data
[
'amount'
]);
}
$model
->
update
([
'status'
=>
$data
[
'status'
]]);
}
}
database/migrations/2023_06_29_065643_create_inscription_trading_table.php
View file @
b05f894e
...
...
@@ -16,19 +16,9 @@ public function up()
Schema
::
create
(
'inscription_trading'
,
function
(
Blueprint
$table
)
{
$table
->
id
();
// 铭文数据
$table
->
text
(
'content_uri'
)
->
comment
(
'铭文数据(data:json)'
);
// 数量(amt)
$table
->
integer
(
'quantity'
)
->
comment
(
'总数量'
);
// 当前持有者
$table
->
string
(
'owner'
)
->
comment
(
'当前持有者'
);
// 创建者
$table
->
string
(
'creator'
)
->
comment
(
'创建者'
);
// 铭文创建时间
$table
->
dateTime
(
'inscription_created_date'
)
->
comment
(
'铭文创建时间'
);
// 铭文上的原始hash
$table
->
string
(
'inscription_hash'
)
->
comment
(
'铭文上的hash'
);
$table
->
text
(
'inscription'
)
->
comment
(
'铭文数据'
);
// 原始金额--卖家输入的
$table
->
integer
(
'original_amount'
)
->
comment
(
'原始金额'
);
$table
->
decimal
(
'original_amount'
,
20
,
8
)
->
comment
(
'原始金额'
);
// 后台收款账号
$table
->
string
(
'admin_account'
)
->
comment
(
'后台收款账号'
);
// 卖出手续费比例
...
...
@@ -57,6 +47,10 @@ public function up()
$table
->
string
(
'buyer_pay_hash'
)
->
nullable
()
->
comment
(
'买家支付hash'
);
// 买家接收铭文hash
$table
->
string
(
'buyer_receive_inscription_hash'
)
->
nullable
()
->
comment
(
'买家接收铭文hash'
);
// 卖家地址
$table
->
string
(
'seller_address'
)
->
nullable
()
->
comment
(
'卖家地址'
);
// 买家地址
$table
->
string
(
'buyer_address'
)
->
nullable
()
->
comment
(
'买家地址'
);
$table
->
timestamps
();
});
...
...
database/migrations/2023_06_29_085620_create_transaction_log_table.php
View file @
b05f894e
...
...
@@ -14,6 +14,8 @@ public function up()
{
Schema
::
create
(
'transaction_log'
,
function
(
Blueprint
$table
)
{
$table
->
id
();
// 订单id(对应市场表的id)
$table
->
integer
(
'order_id'
)
->
comment
(
'订单id'
);
// to
$table
->
string
(
'to'
)
->
comment
(
'收款人地址'
);
//from
...
...
@@ -22,8 +24,10 @@ public function up()
$table
->
string
(
'hash'
)
->
comment
(
'交易hash'
);
// 状态
$table
->
unsignedTinyInteger
(
'status'
)
->
default
(
0
)
->
comment
(
'交易状态'
);
// 交易类型
// 交易类型
(铭文,转账)
$table
->
unsignedTinyInteger
(
'type'
)
->
default
(
0
)
->
comment
(
'交易类型'
);
// title
$table
->
integer
(
'title'
)
->
default
(
0
)
->
comment
(
'交易标题'
);
// 交易金额
$table
->
decimal
(
'amount'
,
20
,
8
)
->
default
(
0
)
->
comment
(
'交易金额'
);
// 错误信息
...
...
resources/js/Pages/InscriptionShelves/components/MyInscription.vue
View file @
b05f894e
This diff is collapsed.
Click to expand it.
resources/js/Pages/InscriptionShelves/components/SellDialog.vue
View file @
b05f894e
...
...
@@ -26,6 +26,20 @@
</div>
<div
class=
"sell-inscription-tips"
>
<div
class=
"label"
>
铭文托管转移
</div>
<div
class=
"all-address"
>
<div>
<span
class=
"all-address-label"
>
从 :
</span>
<span
class=
"all-address-value"
>
{{
userAddress
.
address
}}
</span>
</div>
<div>
<span
class=
"all-address-label"
>
到 :
</span>
<span
class=
"all-address-value"
>
{{
app_info
.
receipt_account
}}
</span>
</div>
</div>
<div
class=
"value"
>
注意事项:由于还未有开放的交易市场,平台所有交易都是以OTC方式进行交易,卖家的铭文
必须托管转移到平台账户,才可进行挂单出售,平台匹配到对应的买家,则会把出售的USDT
...
...
@@ -65,6 +79,7 @@ import { showMessage } from "@/utils/tool";
import
{
inertia_data
}
from
"@/constants/token"
;
import
{
inscriptionTransfer
}
from
"@/utils/ethers"
;
import
{
useStore
}
from
"vuex"
;
import
{
sellerInscriptions
}
from
"@/utils/api/index"
;
const
props
=
withDefaults
(
defineProps
<
{
...
...
@@ -74,7 +89,7 @@ const props = withDefaults(
}
>
(),
{}
);
const
emit
=
defineEmits
([
"update:modelValue"
]);
const
emit
=
defineEmits
([
"update:modelValue"
,
"updateList"
]);
const
store
=
useStore
();
const
userAddress
=
computed
(()
=>
store
.
getters
[
"user/address"
]);
const
visible
=
ref
(
props
.
modelValue
);
...
...
@@ -111,7 +126,29 @@ const onCanel = () => {
visible
.
value
=
false
;
};
const
onSubmit
=
()
=>
{
// 提交到后台
const
submitAdmin
=
async
(
data
:
any
,
price
:
number
|
string
,
current_inscription
:
any
)
=>
{
try
{
const
res
:
any
=
await
sellerInscriptions
({
payment_address
:
data
.
from
,
hash
:
data
.
hash
,
price
:
price
,
inscription
:
current_inscription
,
});
if
(
res
.
code
==
0
)
{
showMessage
(
"已提交,请等待"
,
"success"
);
return
true
;
}
}
catch
(
e
)
{
console
.
log
(
e
);
}
};
const
onSubmit
=
async
()
=>
{
const
{
info
}
=
props
;
if
(
!
price
.
value
)
{
showMessage
(
"请输入价格"
);
...
...
@@ -137,13 +174,26 @@ const onSubmit = () => {
}
// data数据
let
data
=
info
.
content_uri
;
let
data
=
info
.
transaction_hash
;
if
(
!
data
)
{
showMessage
(
"没有data数据"
);
return
;
}
// 记录本次提交的价格,防止被修改
let
current_price
=
price
.
value
;
// 本次铭文信息
let
current_inscription
=
props
.
info
;
// 可以转移
inscriptionTransfer
(
receipt_account
,
from
,
data
);
const
res
=
await
inscriptionTransfer
(
receipt_account
,
from
,
data
);
if
(
res
&&
res
.
hash
)
{
let
status
=
await
submitAdmin
(
res
,
current_price
,
current_inscription
);
if
(
status
)
{
// 关闭弹窗
visible
.
value
=
false
;
// 通知页面更新我的铭文列表
emit
(
"updateList"
);
}
}
};
</
script
>
...
...
@@ -195,7 +245,20 @@ const onSubmit = () => {
.value
{
font-size
:
11px
;
color
:
#858585
;
margin
:
50px
0
12px
0
;
margin
:
20px
0
12px
0
;
}
.all-address
{
font-size
:
15px
;
color
:
#000000
;
&
>
*
{
margin-top
:
6px
;
}
.all-address-label
{
font-weight
:
700
;
}
.all-address-value
{
font-weight
:
400
;
}
}
}
.service-fee-box
{
...
...
resources/js/Pages/InscriptionShelves/components/confirmDialog.vue
0 → 100644
View file @
b05f894e
<
template
>
<t-dialog
v-model:visible=
"visible"
placement=
"center"
class=
"custom-confirm-dialog"
width=
"485px"
>
<template
#
header
>
</
template
>
<
template
#
body
>
<div
class=
"custom-confirm-content"
>
<div
class=
"confirm-title"
>
确定取消订单?
</div>
<div
class=
"confirm-footer"
>
<t-button
class=
"confirm-footer-canenl"
@
click=
"visible = false"
>
取消
</t-button
>
<t-button
class=
"confirm-footer-submit"
@
click=
"onSubmit"
>
确认
</t-button
>
</div>
</div>
</
template
>
<
template
#
footer
>
</
template
>
</t-dialog>
</template>
<
script
lang=
"ts"
setup
>
import
{
ref
,
watch
}
from
"vue"
;
const
props
=
withDefaults
(
defineProps
<
{
modelValue
:
boolean
;
}
>
(),
{}
);
const
emit
=
defineEmits
([
"update:modelValue"
,
"ConfirmCancel"
]);
const
visible
=
ref
(
props
.
modelValue
);
watch
(
()
=>
props
.
modelValue
,
(
v
)
=>
{
visible
.
value
=
v
;
}
);
watch
(
()
=>
visible
.
value
,
(
v
)
=>
{
emit
(
"update:modelValue"
,
v
);
}
);
const
onSubmit
=
()
=>
{
emit
(
"ConfirmCancel"
);
visible
.
value
=
false
;
};
</
script
>
<
style
lang=
"less"
>
.custom-confirm-dialog
{
.t-dialog
{
padding
:
0
;
border-radius
:
8px
;
.t-dialog__header
{
height
:
45px
;
padding
:
0
20px
;
}
.custom-confirm-content
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
center
;
align-items
:
center
;
.confirm-title
{
color
:
#000000
;
font-size
:
20px
;
}
.confirm-footer
{
margin-top
:
20px
;
.confirm-footer-canenl
{
width
:
80px
;
height
:
40px
;
border-radius
:
5px
;
border
:
1px
solid
#dedede
;
background
:
transparent
;
color
:
#000000
;
--ripple-color
:
#ddd
!important
;
margin-right
:
20px
;
}
.confirm-footer-submit
{
width
:
80px
;
height
:
40px
;
border-radius
:
5px
;
border
:
1px
solid
#dedede
;
background
:
#235ffa
;
color
:
#ffffff
;
border-color
:
#235ffa
;
}
}
}
}
}
</
style
>
resources/js/Pages/Trade/components/BuyDialog.vue
View file @
b05f894e
...
...
@@ -15,7 +15,7 @@
<div
class=
"service-fee-box"
>
<div
class=
"service-fee-line"
>
<div
class=
"label"
>
价值
</div>
<div
class=
"value"
>
$
{{
info
.
price
}}
</div>
<div
class=
"value"
>
$
{{
info
.
original_amount
}}
</div>
</div>
<div
class=
"service-fee-line"
>
<div
class=
"label"
>
服务费
</div>
...
...
@@ -32,16 +32,25 @@
</
template
>
<
template
#
footer
>
<div
class=
"buy-dialog-footer"
>
<t-button
class=
"buy-cancel-button"
>
取消
</t-button>
<t-button
class=
"buy-confirm-button"
>
确认
</t-button>
<t-button
class=
"buy-cancel-button"
@
click=
"visible = false"
>
取消
</t-button
>
<t-button
class=
"buy-confirm-button"
@
click=
"onSubmit"
>
确认
</t-button
>
</div>
</
template
>
</t-dialog>
</template>
<
script
lang=
"ts"
setup
>
import
{
computed
,
ref
,
watch
}
from
"vue"
;
import
{
computed
,
inject
,
ref
,
watch
}
from
"vue"
;
import
CustomCard
from
"@/components/card.vue"
;
import
{
fox_EtherPay
}
from
"@/utils/ethers"
;
import
{
useStore
}
from
"vuex"
;
import
{
inertia_data
}
from
"@/constants/token"
;
import
{
showMessage
}
from
"@/utils/tool"
;
import
{
buyerPaymentSuccess
}
from
"@/utils/api"
;
const
props
=
withDefaults
(
defineProps
<
{
...
...
@@ -52,21 +61,67 @@ const props = withDefaults(
{}
);
const
emit
=
defineEmits
([
"update:modelValue"
]);
const
store
=
useStore
();
const
visible
=
ref
(
props
.
modelValue
);
// app
const
app_info
=
inject
(
inertia_data
);
// 点击确认时记录输入框输入的价格,防止被修改
const
confirmPrice
=
ref
(
""
);
const
userAddress
=
computed
(()
=>
store
.
getters
[
"user/address"
]);
// 根据输入的价格计算可获得usdt
const
realPrice
=
computed
(()
=>
{
const
{
fee
,
info
}
=
props
;
if
(
info
.
price
&&
fee
)
{
if
(
info
.
original_amount
&&
fee
)
{
return
(
parseFloat
(
info
.
price
+
""
)
+
info
.
price
*
(
parseFloat
(
fee
+
""
)
/
100
)
parseFloat
(
info
.
original_amount
+
""
)
+
info
.
original_amount
*
(
parseFloat
(
fee
+
""
)
/
100
)
);
}
});
//
const
submitAdmin
=
async
(
hash
:
string
,
address
:
string
,
id
:
string
|
number
)
=>
{
try
{
let
res
:
any
=
await
buyerPaymentSuccess
({
payment_address
:
address
,
hash
:
hash
,
id
:
id
,
});
if
(
res
.
code
==
0
)
{
showMessage
(
"已提交,请等待"
);
return
;
}
}
catch
(
e
)
{
console
.
log
(
e
);
}
};
// 立即购买
const
onSubmit
=
async
()
=>
{
// 付款账号
let
address
=
userAddress
.
value
.
address
;
// id
let
id
=
props
.
info
.
id
;
// 收款地址
let
receipt_account
=
app_info
.
receipt_account
;
if
(
!
receipt_account
)
{
showMessage
(
"缺少必要的数据"
);
return
;
}
const
hash
=
await
fox_EtherPay
(
receipt_account
,
realPrice
.
value
);
if
(
hash
)
{
// 提交到后台校验
submitAdmin
(
hash
,
address
,
id
);
}
console
.
log
(
hash
);
};
watch
(
()
=>
props
.
modelValue
,
(
v
)
=>
{
...
...
resources/js/Pages/Trade/components/Market.vue
View file @
b05f894e
...
...
@@ -18,7 +18,9 @@
<CustomCard
:cardData=
"item"
>
<template
#
footer
>
<div
class=
"buy-card-footer"
>
<div
class=
"price"
>
$
{{
item
.
price
}}
</div>
<div
class=
"price"
>
$
{{
item
.
original_amount
}}
</div>
<t-button
@
click=
"buyNow(item)"
class=
"buy-now-button"
...
...
@@ -33,30 +35,35 @@
<BuyDialog
v-model=
"dialog_info.status"
:info=
"dialog_info.info"
:fee=
"
current
_fee"
:fee=
"
app_info.buy
_fee"
></BuyDialog>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
CustomSelect
from
"@/components/select.vue"
;
import
{
ref
,
reactive
,
onMounted
}
from
"vue"
;
import
{
ref
,
reactive
,
onMounted
,
inject
}
from
"vue"
;
import
CustomCard
from
"@/components/card.vue"
;
import
{
test_wallet_list
}
from
"@/constants/testData"
;
import
{
isDev
}
from
"@/utils/tool"
;
import
{
inscriptionListFilter
}
from
"@/utils/api/public"
;
import
{
inscriptionListFilter
,
inscriptionListAdminFilter
,
}
from
"@/utils/api/public"
;
import
BuyDialog
from
"./BuyDialog.vue"
;
import
{
getMarketInscriptionList
}
from
"@/utils/api"
;
import
{
inertia_data
}
from
"@/constants/token"
;
const
imgs
=
{
eth
:
new
URL
(
"../../../assets/svg/trade/eth2.svg"
,
import
.
meta
.
url
).
href
,
};
// 当前select
const
selectValue
=
ref
(
"asc"
);
// 接收app数据
const
app_info
=
inject
(
inertia_data
);
const
dialog_info
=
reactive
({
status
:
false
,
info
:
{},
});
// 手续费
const
current_fee
=
ref
(
"1"
);
const
options
=
[
{
label
:
"从低到高"
,
...
...
@@ -82,30 +89,28 @@ const buyNow = (item: any) => {
};
const
listResult
=
(
list
:
any
[])
=>
{
// 过滤数据
list
=
inscriptionListFilter
(
list
);
// 添加测试数据
list
=
inscriptionListAdminFilter
(
list
);
list
.
forEach
((
item
:
any
)
=>
{
item
.
price
=
"300"
;
item
.
original_amount
=
parseFloat
(
item
.
original_amount
+
""
)
;
});
tableList
.
list
=
inscriptionListFilter
(
list
)
;
tableList
.
list
=
list
;
tableList
.
total
=
list
.
length
;
};
const
getList
=
async
()
=>
{
try
{
if
(
isDev
())
{
// 本地环境
listResult
(
test_wallet_list
);
}
else
{
tableList
.
loading
=
true
;
const
res
:
any
=
await
getUserInscriptionList
(
address
);
if
(
res
)
{
let
list
=
JSON
.
parse
(
res
);
if
(
list
.
length
)
{
listResult
(
list
);
}
tableList
.
loading
=
true
;
const
res
:
any
=
await
getMarketInscriptionList
({
page
:
tableList
.
pageNum
,
limit
:
tableList
.
pageSize
,
});
if
(
res
.
code
==
0
)
{
if
(
res
.
data
.
data
.
length
)
{
listResult
(
res
.
data
.
data
);
}
else
{
listResult
([]);
}
tableList
.
loading
=
false
;
}
tableList
.
loading
=
false
;
}
catch
(
e
)
{
console
.
log
(
e
);
tableList
.
loading
=
false
;
...
...
resources/js/Pages/Trade/index.vue
View file @
b05f894e
...
...
@@ -18,7 +18,7 @@ import { Head } from "@inertiajs/vue3";
import
Navbar
from
"@/components/navbar.vue"
;
import
CustomTable
from
"@/components/table.vue"
;
import
CustomInput
from
"@/components/input/index.vue"
;
import
{
reactive
,
ref
}
from
"vue"
;
import
{
reactive
,
ref
,
provide
}
from
"vue"
;
import
CustomGroupButton
from
"@/components/groupButton.vue"
;
import
TipsSvg
from
"@/assets/svg/content/tips.svg"
;
import
CustomTooltip
from
"@/components/tooltip.vue"
;
...
...
@@ -30,9 +30,11 @@ import TradeHeader from "./components/TradeHeader.vue";
import
CustomTabs
from
"@/components/tabs.vue"
;
import
MarketVue
from
"./components/Market.vue"
;
import
TradeLog
from
"./components/TradeLog.vue"
;
defineProps
({
info
:
Object
});
import
{
inertia_data
}
from
"@/constants/token"
;
const
props
=
defineProps
({
info
:
Object
});
// 请求接口放这里
//注入
provide
(
inertia_data
,
props
.
info
);
const
tabList
=
[
{
...
...
resources/js/components/card.vue
View file @
b05f894e
...
...
@@ -42,6 +42,7 @@
</div>
</div>
<slot
name=
"footer"
>
</slot>
<slot
name=
"pos"
>
</slot>
</div>
</template>
...
...
@@ -61,6 +62,7 @@ const props = withDefaults(
border
:
1px
solid
#ebebeb
;
box-sizing
:
border-box
;
box-shadow
:
0px
4px
25px
10px
#0000000
d
;
position
:
relative
;
.inscription-card-head
{
background
:
rgb
(
250
,
250
,
250
);
...
...
resources/js/utils/api/index.ts
View file @
b05f894e
...
...
@@ -10,3 +10,26 @@ export const getUserInscriptionList = (address: string) => {
};
// 卖家上架铭文
export
const
sellerInscriptions
=
(
data
:
any
)
=>
{
return
request
.
post
(
"/api/inscription/shelves"
,
data
);
};
// 卖家交易记录
export
const
sellerTradeLog
=
(
data
:
any
)
=>
{
return
request
.
get
(
"/api/inscription/sell/log"
,
{
params
:
data
,
});
};
// 获取市场上的铭文交易列表
export
const
getMarketInscriptionList
=
(
data
:
any
)
=>
{
return
request
.
get
(
"/api/inscription/market"
,
{
params
:
data
,
});
};
// 买家支付
export
const
buyerPaymentSuccess
=
(
data
:
any
)
=>
{
//
return
request
.
post
(
"/api/inscription/buy"
,
data
);
};
resources/js/utils/api/public.ts
View file @
b05f894e
import
{
getMask
,
timeFormatSeconds
,
getCreatedHowLongAgo
}
from
"@/utils/tool"
;
// 铭文列表过滤
// 铭文列表过滤
外部接口的
export
const
inscriptionListFilter
=
(
list
:
any
[])
=>
{
list
.
forEach
((
item
:
any
)
=>
{
// 创建者,掩码
...
...
@@ -29,3 +29,48 @@ export const inscriptionListFilter = (list: any[]) => {
});
return
list
;
};
// 铭文列表过滤,平台内部接口
export
const
inscriptionListAdminFilter
=
(
list
:
any
[])
=>
{
try
{
list
.
forEach
((
item
:
any
)
=>
{
let
inscription
=
item
.
inscription
;
// 创建者,掩码
item
.
new_creator
=
getMask
(
inscription
.
creator
,
6
,
4
);
// 当前持有,掩码
item
.
new_current_owner
=
getMask
(
inscription
.
current_owner
,
4
,
4
);
item
.
creation_timestamp
=
timeFormatSeconds
(
inscription
.
creation_timestamp
);
// 距离当前时间多久
item
.
created_time
=
getCreatedHowLongAgo
(
inscription
.
creation_timestamp
);
// hash
item
.
new_transaction_hash
=
getMask
(
inscription
.
transaction_hash
,
4
,
4
);
// 去掉换行符和空格
item
.
new_content_uri
=
inscription
.
content_uri
.
replace
(
/
\r\n
/g
,
""
);
// 过滤出名称和数量
const
match
=
inscription
.
content_uri
.
match
(
/{.*}/
);
if
(
match
)
{
const
jsonStr
=
match
[
0
];
const
jsonObject
=
JSON
.
parse
(
jsonStr
);
item
.
name
=
jsonObject
.
tick
;
item
.
number
=
jsonObject
.
amt
;
item
.
id
=
jsonObject
.
id
;
}
else
{
item
.
image
=
inscription
.
content_uri
;
console
.
log
(
"content_uri格式错误"
);
console
.
log
(
inscription
.
content_uri
);
}
});
}
catch
(
e
)
{
console
.
log
(
e
);
}
return
list
;
};
resources/js/utils/ethers.ts
View file @
b05f894e
...
...
@@ -46,23 +46,28 @@ export const inscriptionTransfer = async (
data
:
any
)
=>
{
// data转16进制
let
data16
=
ConvertToHexadecimal
(
data
);
console
.
log
(
data16
);
// let data16 = ConvertToHexadecimal(data);
try
{
const
res
:
any
=
await
eth
.
ethereum
.
request
({
const
hash
:
any
=
await
eth
.
ethereum
.
request
({
method
:
"eth_sendTransaction"
,
params
:
[
{
from
:
from
,
to
:
to
,
value
:
0
,
data
:
data
16
,
data
:
data
,
// gasLimit: "0x5028",
// maxPriorityFeePerGas: "0x3b9aca00",
// maxFeePerGas: "0x2540be400",
},
],
});
if
(
hash
)
{
return
{
hash
:
hash
,
from
:
from
,
};
}
// 获取交易hash
}
catch
(
e
:
any
)
{
console
.
log
(
e
);
...
...
routes/api.php
View file @
b05f894e
...
...
@@ -28,6 +28,14 @@
Route
::
get
(
'/ethscriptions/owned_by'
,
'InscriptionController@wallet'
);
// 铭文上架
Route
::
post
(
'/shelves'
,
'TradeController@shelves'
);
// 交易检测回调
Route
::
post
(
'/admin/check'
,
'TradeController@check'
);
// 卖家交易记录
Route
::
get
(
'/sell/log'
,
'TradeController@search'
);
// 获取市场上的铭文列表
Route
::
get
(
'/market'
,
'TradeController@inscriptionList'
);
// buy
Route
::
post
(
'/buy'
,
'TradeController@buy'
);
});
// // 需要登录
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment