现在去中心化的交易越来越火,去中心化交易把卖家和买家之间,交易执行的过程写到智能合约里,没有人能够篡改这个合约,完全避开了中心化交易所的控制,使交易更加透明,自由。因此先写了一个简单的去中心交易合约,具体代码如下:
pragma solidity >=0.4.22 <0.7.0;
/**
* 交易合约
* 每个价格只能有1个人
* 如果是买代币,输入价格和数量,并转入基础币
* 如果是卖代币,输入价格和数量,并转入代币
*/
contract Trade {
address owner = msg.sender; //保存合约创建人
mapping(uint => order) public buyList;//买单列表,key为档位,order为下单相关信息
mapping(uint => order) public sellList;//卖单列表,key为档位,order为下单相关信息
bool isAddBuyList;//是否加入买单队列
bool isAddSellList;//是否加入卖单队列
uint assertId;//需要交易的币种的资产id
bool isInit;//是否初始化
struct order{
uint price; //下单价格
uint amount; //下单数量
address addr; //下单人
}
order buyRemove;//需要移除买队列的订单
order sellRemove;//需要移除卖队列的订单
/**
* 初始化函数
* _assetId:资产id
*/
function init(uint _assetId) public{
require(owner==msg.sender);//必须为合约所有者
require(isInit==false);//必须没有初始化过
isInit=true;//设置初始化为true
assertId=_assetId;
}
// 获取买单
function getBuyList(uint num) public returns (uint,uint){
return (buyList[num].price,buyList[num].amount);
}
// 获取卖单
function getSellList(uint num) public returns (uint,uint){
return (sellList[num].price,sellList[num].amount);
}
// 撮合
function matchTrade() internal{
for (uint num=0;num<10;num++){
// 获取买的价格
uint buyPrice=buyList[0].price;
uint buyAmount=buyList[0].amount;
uint sellPrice=sellList[0].price;
uint sellAmount=sellList[0].amount;
bool isTrade;//是否成交
if(sellPrice==0){
return;
}
if (buyPrice==0){
return;
}
//如果买价格大于卖价格,则成交,撮合
if (buyPrice>=sellPrice){
isTrade=true;
//最终成交的数量
uint sendAmount;
// 如果买单数量大于卖单数量
if (buyAmount>=sellAmount){
//统一按照买单价格转账
//更新买单数量和卖单数量
buyList[0].amount=buyAmount-sellAmount;
sellList[0].amount=0;
sendAmount=sellAmount;
}else{
//更新买单数量和卖单数量
buyList[0].amount=0;
sellList[0].amount=sellAmount-buyAmount;
sendAmount=buyAmount;
}
//给买方转代币
buyList[0].addr.transfer(assertId,sendAmount);
//给卖方转基础币
uint c=buyPrice*sendAmount;
sellList[0].addr.transfer(0,c);
}
// 只有成交了才对买卖盘进行整理
if (isTrade==true){
// 对买盘进行整理
// 如果买1的数量为0,则将后面的全部往前挪
if (buyList[0].amount==0){
for (uint ii=0;ii<9;ii++){
buyList[ii]=buyList[ii+1];
}
// 将最后一个数据删除
delete buyList[9];
}
// 对卖盘进行整理
if (sellList[0].amount==0){
for (uint j=0;j<9;j++){
sellList[j]=sellList[j+1];
}
// 将最后一个数据删除
delete sellList[9];
}
}
}
}
//买操作
function buy(uint price,uint amount) public payable{
//转入的金额需要大于等于价格*数量
require(price*amount<=msg.value);
//转账的资产id必须正确,买单必须是主资产
require(msg.assetid==0);
isAddBuyList=false;
//先判断价格在哪个范围
//如果价格大于买1,则买1~买9一次往后挪,新的数据为买1
for (uint j=0;j<10;j++){
// 如果输入的价格大于当前档位,则当前档位后移
if (buyList[j].price<price){
//将需要移除的数据保存
buyRemove=buyList[9];
//加入买单队列,将标志设置为true
isAddBuyList=true;
// 将正确价格后面的数据后移
for(uint i=9;i>j;i--){
buyList[i]=buyList[i-1];
}
//将新的订单赋值到对应位置
buyList[j].price=price;
buyList[j].amount=amount;
buyList[j].addr=msg.sender;
//跳出循环
break;
}
//如果存在相同价格,则直接退币
if(buyList[j].price==price){
//msg.sender.transfer(0,msg.value);
break;
}
}
//如果没有加入买单队列,则把钱退回
if (isAddBuyList==false){
msg.sender.transfer(0,msg.value);
}else{
if (buyRemove.amount!=0){
//如果有人加入了队列,需要把原来的人的钱返回
buyRemove.addr.transfer(0,buyRemove.price*buyRemove.amount);
}
// 有人加入队列才调用撮合
matchTrade();
}
}
//卖操作
function sell(uint price,uint amount) public payable{
//转入资产必须是当前合约交易的资产
require(assertId==msg.assetid);
require(amount==msg.value);//必须足额转入
isAddSellList=false;
//先判断价格在哪个范围
//如果价格小于卖1,则卖1~卖9一次往后挪,新的数据为买1
for (uint j=0;j<10;j++){
//当前档位没有数据
if (sellList[j].price==0){
//将需要移除的数据保存
sellRemove=sellList[9];
//加入卖单队列,将标志设置为true
isAddSellList=true;
//将新的订单赋值到对应位置
sellList[j].price=price;
sellList[j].amount=amount;
sellList[j].addr=msg.sender;
//跳出循环
break;
}else{
if(sellList[j].price>price){
//将需要移除的数据保存
sellRemove=sellList[9];
//加入卖单队列,将标志设置为true
isAddSellList=true;
// 将正确价格后面的数据后移
for(uint i=9;i>j;i--){
sellList[i]=sellList[i-1];
}
//将新的订单赋值到对应位置
sellList[j].price=price;
sellList[j].amount=amount;
sellList[j].addr=msg.sender;
//跳出循环
break;
}
//如果存在相同价格,则直接退币
if(sellList[j].price==price){
//msg.sender.transfer(assertId,msg.value);
break;
}
}
}
//如果没有加入买单队列,则把钱退回
if (isAddSellList==false){
msg.sender.transfer(assertId,msg.value);
}else{
if (sellRemove.amount!=0){
//如果有人加入了队列,需要把原来的人的钱返回
sellRemove.addr.transfer(assertId,sellRemove.amount);
}
// 有人加入队列才调用撮合
matchTrade();
}
}
/**
* 功能:撤单
* orderType:订单类型,buy,sell
* price:价格
*/
function cancel(string orderType,uint price) public{
//如果撤销买单
if(orderType=="buy"){
for (uint i=0;i<10;i++){
//用户相同,并且价格相同
if((buyList[i].addr==msg.sender) && (buyList[i].price==price)){
//将剩余的钱返回
msg.sender.transfer(0,buyList[i].price*buyList[i].amount);
//将后面的数据往前移
for (uint ii=i;ii<9;ii++){
buyList[ii]=buyList[ii+1];
}
// 将最后一个数据删除
delete buyList[9];
break;
}
}
}else{
//如果撤销卖单
if(orderType=="sell"){
for (uint j=0;j<10;j++){
//先确定用户是否相同
if((sellList[j].addr==msg.sender) && (sellList[j].price==price)){
//将剩余的钱返回
msg.sender.transfer(assertId,sellList[j].amount);
//将后面的数据往前移
for (uint jj=j;jj<9;jj++){
sellList[jj]=sellList[jj+1];
}
// 将最后一个数据删除
delete sellList[9];
break;
}
}
}
}
}
}
代码中有详细的注释,在此就不再一一的讲解代码,大家自行阅读就可以。有不明白的地方欢迎留言。
这里有几个问题不太合理:
1.每个档位只能有1个订单,这在显示中是不可能的
2.买盘和卖盘最多只能有10个档位,如果想再次下单,则不能成功,或者是把原来的订单挤掉,这个也不太合理
因此在下个版本中,会想办法修复这些问题。
起始这里面的关键是撮合,如何更高效的撮合,花费更小的gas。这将是后期研究的重点。