The AGAINST Project aims to develop a fully decentralized ecosystem for crypto assets, including currency, exchange and site.
The frontend is new decentralized Ethereum Token Exchange, with changes that differentiate the need of a central host and / or domain, being able to IPFS or localhost implementation. This does not prevent anyone from making the gateway available in a domain if they wish. The important thing is the freedom to choose the implementation you find most appropriate.
On Chain Database
- No server;
- Anyone can add an Ethereum token;
- Anyone can add a market to any token;
- Like / dislike voting system to assess reputation. One vote per wallet;
- The contract does not censor any token, but the gateway owner can use the reputation as a filter if he so desires;
- The cost of warehousing per transaction can be offset by low brokerage fees;
- No tokens custody is required. Great for anyone who wants to keep their orders for a long time, waiting for the asset to reach the desired price (Holder traders); -No key or signature is stored in the contract;
- Future expandable to support a payment system that accepts any Ethereum token.
Require Metamask https://against.network/newdex/ Now working on MainNet & Ropsten Network (testnet)
Contract Details: https://github.com/deflatcoin/ethereum-contracts/blob/master/serverlessDexContract.md
CSS Block https://github.com/deflatcoin/decentralization/blob/master/css-block.css
HTML Container https://github.com/deflatcoin/decentralization/blob/master/html-container.html
ABI Block https://github.com/deflatcoin/decentralization/blob/master/abi-block.js
Getting the token list from the database
- Contract side Solidity:
function getTokenByAddr(address _addr) public view returns (string _name, string _symbol, uint _decimals, uint _marketsCount) { return (tokens[_addr].name, tokens[_addr].symbol, tokens[_addr].decimals, tokens[_addr].marketsCount); } function getTokenByIndex(uint _index) public view returns (address _tokenBase, string _name, string _symbol, uint _decimals, uint _marketsCount) { return (tokens[index[_index]].tokenBase, tokens[index[_index]].name, tokens[index[_index]].symbol, tokens[index[_index]].decimals, tokens[index[_index]].marketsCount); }
1- address: Token address;
2- name: Friendly token name, recovered from contract;
3- symbol: Short token name, recovered from contract;
4- decimals: Decimal places of token, recovered from contract;
5- likes count & dislikes count: token reputation for gateway filter (optional);
6- markets count: Incemented to each market created;
7- marketIndex: Index position to address of market struct;
8- markets: market struct mapped by address;
9- voteStatus: indicates if an account has already voted for the token;
- Web side JS:
function listTokens(container) { document.getElementById(container).innerHTML += "SELECT TOKEN"; exchangeContract.indexCount((error,indexCount) => { var html; var i; for (i = 1; i <= indexCount; i++) { exchangeContract.index.call(i, function (err,addr) { exchangeContract.tokens.call(addr, function (err,token) { if (baseDefault.toUpperCase() == token[0].toUpperCase()) { document.getElementById(container).innerHTML += ""+token[1].toUpperCase()+""; listTokenMarkets(); } else { document.getElementById(container).innerHTML += ""+token[1].toUpperCase()+""; } }); }); } }); }
Getting the Market list from the database
- Contract side Solidity:
function getPairByAddr(address _base, address _pairAddr) public view returns (uint _ordersCount, uint _donesCount, bool _exists) { return (tokens[_base].markets[_pairAddr].ordersCount, tokens[_base].markets[_pairAddr].donesCount, tokens[_base].markets[_pairAddr].exists); }
1- _base: token base;
2- _pairAddr: pair address;
3- return: return count of orders and excuted orders for the given pair.
function getPairByIndex(address _base, uint _pairIndex) public view returns (address _tokenPair, uint _ordersCount, uint _donesCount) { return (tokens[_base].markets[tokens[_base].marketIndex[_pairIndex]].tokenPair, tokens[_base].markets[tokens[_base].marketIndex[_pairIndex]].ordersCount, tokens[_base].markets[tokens[_base].marketIndex[_pairIndex]].donesCount); }
1- _base: token base;
2- _pairAddr: pair address
3- return: return count of orders and excuted orders for the given pair;
4- use with marketsCount in token data to get list of markets for a given pair.
- Web side JS:
function listTokenMarkets() { var html; var i; clearFields(); addr = document.getElementById("tkaddress").value; baseName = document.getElementById("tkaddress").options[document.getElementById("tkaddress").selectedIndex].text; exchangeContract.tokens.call(addr, function (err,token) { baseDecimals = token[3]; baseSymbol = token[2]; }); getLikesBase(); document.getElementById("baseCoinTitle").innerText = baseName; document.getElementById('baseCoin').value = addr; document.getElementById("tkpairaddress").innerHTML = ""; if (addr != "SELECT") { getMyBalance(addr,web3.eth.accounts[0], 'myBaseBalance'); exchangeContract.tokens.call(addr, function (err,token) { baseDecimals = token[3]; baseSymbol = token[2]; marketCount = token[6]; if (token[6] == 0) { document.getElementById("tkpairaddress").innerHTML = "NO MARKET"; document.getElementById("tkpairaddress").selectedIndex = 1; } for (i=1; i <= marketCount; i++) { document.getElementById("tkpairaddress").innerHTML = "SELECT MARKET"; exchangeContract.getPairByIndex.call(addr, i, function (err,market) { exchangeContract.tokens.call(market[0], function (err,token) { if (pairDefault.toUpperCase() == token[0].toUpperCase()) { document.getElementById("tkpairaddress").innerHTML += ""+token[1].toUpperCase()+""; listOrders(true); } else { document.getElementById("tkpairaddress").innerHTML += ""+token[1].toUpperCase()+""; } }); }); } }); } else { document.getElementById("tkpairaddress").innerHTML = "NO MARKET"; document.getElementById("tkpairaddress").selectedIndex = 1; } }
Getting order list
- Contract side Solidity:
function getOrders(address _base, address _pair, uint _orderIndex) public view returns (uint _orderId, address _owner, uint _rate, uint _amount, bool _sell) { return (tokens[_base].markets[_pair].orders[_orderIndex].orderId, tokens[_base].markets[_pair].orders[_orderIndex].orderOwner, tokens[_base].markets[_pair].orders[_orderIndex].rate, tokens[_base].markets[_pair].orders[_orderIndex].amount, tokens[_base].markets[_pair].orders[_orderIndex].sell); }
1- Get data from order record;
2- Use with ordersCount to get list of orders.
- Web side JS:
function listOrders(clear) { var html; var i; loading = true; asyncElmDetected = 0; asyncElmLoaded = 0; if (clear) { clearFields(); setCookie("baseDefaultAddr",document.getElementById("tkaddress").value,10); setCookie("pairDefaultAddr",document.getElementById("tkpairaddress").value,10); } else { document.getElementById("typeSell").innerHTML = ''; document.getElementById("typeBuy").innerHTML = ''; } addr = document.getElementById("tkaddress").value; baseName = document.getElementById("tkaddress").options[document.getElementById("tkaddress").selectedIndex].text; pairAddr = document.getElementById("tkpairaddress").value; pairName = document.getElementById("tkpairaddress").options[document.getElementById("tkpairaddress").selectedIndex].text; exchangeContract.tokens.call(pairAddr, function (err,token) { pairDecimals = token[3]; pairSymbol = token[2]; getAllowanceAccount(pairAddr,web3.eth.accounts[0],'approvePairValue'); }); getLikesPair(); getMyBalance(addr,web3.eth.accounts[0], 'myBaseBalance'); getMyBalance(pairAddr,web3.eth.accounts[0], 'myPairBalance'); document.getElementById("btnApproveBase").innerText = baseName; document.getElementById("btnApprovePair").innerText = pairName; document.getElementById("btnApproveBase").title = 'Approve value to '+baseName; document.getElementById("btnApprovePair").title = 'Approve value to '+pairName; document.getElementById('baseExplorerLink').href = 'https://'+ropstenDomain+'etherscan.io/address/'+addr; document.getElementById('pairExplorerLink').href = 'https://'+ropstenDomain+'etherscan.io/address/'+pairAddr; document.getElementById("sellHeader").innerHTML = 'THEY OFFER '+baseName+'RATE/SORTREFRESHTHEY WANTS '+pairName+'FUNDS: '+baseName+'ALLOWANCE '+baseName+''; document.getElementById("buyHeader").innerHTML = 'THEY OFFER '+pairName+'RATE/SORTREFRESHTHEY WANTS '+baseName+'FUNDS: '+pairName+'ALLOWANCE '+pairName+''; if ((addr != "SELECT") && (pairAddr != "SELECT")) { getAllowanceAccount(addr, web3.eth.accounts[0],'approveBaseValue'); document.getElementById('pairSymbolSell').innerText = 'SEND '+baseName; document.getElementById("pairSymbolSellPlaced").innerText = 'TO GET '+pairName; document.getElementById('pairSymbolBuy').innerText = 'SEND '+pairName; document.getElementById("pairSymbolBuyPlaced").innerText = 'TO GET '+baseName; exchangeContract.getPairByAddr(addr, pairAddr, function (err,market) { ordersCount = market[0]; ordersCountOld = parseInt(ordersCount); donesCountOld = parseInt(market[1]); asyncElmDetected = asyncElmDetected+(ordersCount*3); for (i=1; i <= ordersCount; i++) { exchangeContract.getOrders(addr, pairAddr, i, function (err, orders) { if (orders[1] == web3.eth.accounts[0]) { fillLbl = 'YOU'; } else { fillLbl = 'FILL'; } document.getElementById("typeSell").innerHTML += ''+(orders[3]/(10**baseDecimals)).toFixed(decimalPlaces)+''+(orders[2]/(10**9)).toFixed(9)+''+fillLbl+''+(((orders[3]/(10**baseDecimals))/orders[2])*(10**9)).toFixed(decimalPlaces)+''+getBalance(addr,orders[1],orders[0],'a',1,orders[3])+''+getAllowance(pairAddr,addr,orders[1],exchangeAddr,orders[0], orders[1],'a',1,baseDecimals,orders[3])+''; }); } }); exchangeContract.getPairByAddr(pairAddr, addr, function (err,market) { ordersCount = market[0]; asyncElmDetected = asyncElmDetected+(ordersCount*3); if (market[2]) { } for (i=1; i <= ordersCount; i++) { exchangeContract.getOrders(pairAddr, addr, i, function (err, orders) { if (orders[1] == web3.eth.accounts[0]) { fillLbl = 'YOU'; } else { fillLbl = 'FILL'; } document.getElementById("typeBuy").innerHTML += ''+(orders[3]/(10**pairDecimals)).toFixed(decimalPlaces)+''+(1/(orders[2]/(10**9))).toFixed(9)+''+fillLbl+''+(((orders[3]/(10**pairDecimals))/orders[2])*(10**9)).toFixed(decimalPlaces)+''+getBalance(pairAddr,orders[1],orders[0],'b',0,orders[3])+''+getAllowance(addr,pairAddr,orders[1],exchangeAddr,orders[0], orders[1],'b',0,pairDecimals,orders[3])+''; }); } }); } else { document.getElementById("tkpairaddress").innerHTML = "NO MARKET"; } listOrdersDones(); }
Getting done list
- Contract side Solidity
function getDones(address _base, address _pair, uint _doneIndex) public view returns (uint _orderId, address _fillOwner, uint _fillAmount, uint _fillDate, uint _rate) { return (tokens[_base].markets[_pair].dones[_doneIndex].orderId, tokens[_base].markets[_pair].dones[_doneIndex].fillOwner, tokens[_base].markets[_pair].dones[_doneIndex].fillAmount, tokens[_base].markets[_pair].dones[_doneIndex].fillDate, tokens[_base].markets[_pair].dones[_doneIndex].rate); }
1- Get data from executed order record;
2- Use with donesCout to get list of executed orders.
- Web side JS
function listOrdersDones() { var html; var i; document.getElementById("typeSellDones").innerHTML = ''; document.getElementById("typeBuyDones").innerHTML = ''; addr = document.getElementById("tkaddress").value; pairAddr = document.getElementById("tkpairaddress").value; document.getElementById("sellHeaderDones").innerHTML = ''+baseName+'RATEDATE'; document.getElementById("buyHeaderDones").innerHTML = ''+pairName+'RATEDATE'; if ((addr != "SELECT") && (pairAddr != "SELECT")) { exchangeContract.getPairByAddr(addr, pairAddr, function (err,market) { ordersCount = market[1]; document.getElementById('donesCount1').innerText = ordersCount; start = ordersCount-4; // last 5 if (start < 1) {start = 1} for (i=start; i <= ordersCount; i++) { exchangeContract.getDones(addr, pairAddr, i, function (err, orders) { document.getElementById("typeSellDones").innerHTML += ''+(orders[2]/(10**pairDecimals)).toFixed(9)+''+(orders[4]/(10**9)).toFixed(5)+''+orders[3]+''; }); } }); exchangeContract.getPairByAddr(pairAddr, addr, function (err,market) { ordersCount = market[1]; document.getElementById('donesCount2').innerText = ordersCount; start = ordersCount-4; // last 5 if (start < 1) {start = 1} for (i=start; i <= ordersCount; i++) { exchangeContract.getDones(pairAddr, addr, i, function (err, orders) { document.getElementById("typeBuyDones").innerHTML += ''+(orders[2]/(10**baseDecimals)).toFixed(9)+''+(orders[4]/(10**9)).toFixed(5)+''+orders[3]+''; }); } }); } }
Starting App
- Web side JS:
const urlParams = new URLSearchParams(window.location.search); startBaseDefault = "0xe1E0DB951844E7fb727574D7dACa68d1C5D1525b"; startPairDefault = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; var baseName = ""; var pairName = ""; function link(domain) { var loc = window.location.pathname; var scriptfolder = loc.substring(0, loc.lastIndexOf('/')); document.write(""+domain+""); } function setCookie(cookie_name, cookie_value, expire_in_days) { var cookie_expire = ""; if (expire_in_days != null) { var expire = new Date(); expire.setTime(expire.getTime() + 1000*60*60*24*parseInt(expire_in_days)); cookie_expire = "; expires=" + expire.toGMTString(); } document.cookie = escape(cookie_name) + "=" + escape(cookie_value) + cookie_expire; } function getCookie(cookie_name) { if (!document.cookie.match(eval("/" + escape(cookie_name) + "=/"))) { return false; } return unescape(document.cookie.replace(eval("/^.*?" + escape(cookie_name) + "=([^\\s;]*).*$/"), "$1")); } if (urlParams != "") { baseDefault = urlParams.get('baseAddr'); pairDefault = urlParams.get('pairAddr'); } else { cookieBase = getCookie("baseDefaultAddr"); cookiePair = getCookie("pairDefaultAddr"); if (!cookieBase) { baseDefault = startBaseDefault; } else { baseDefault = cookieBase; } if (!cookiePair) { pairDefault = startPairDefault; } else { pairDefault = cookiePair; } } function orderWatch() { if ((addr != "SELECT") && (pairAddr != "SELECT")) { exchangeContract.getPairByAddr(addr, pairAddr, function (err,market) { ordersCountNew = market[0]; donesCountNew = market[1]; if (parseInt(ordersCountNew) != parseInt(ordersCountOld)) { ordersCountOld = parseInt(ordersCountNew); donesCountOld = parseInt(donesCountNew); if (!loading) { listOrders(false); } } else { if (parseInt(donesCountNew) != parseInt(donesCountOld)) { donesCountOld = parseInt(donesCountNew); if (!loading) { listOrders(false); } } } }); } } function startEx() { web3.version.getNetwork((err, netId) => { if (netId == 1) { ropsten = false; opstenDomain = ""; printStatus(false, 'MainNet Selected!'); exchangeAddr = "0x2dcf69b59c2301Dd2bC632F7F9f3EB7b93b98E31"; wrapAddr = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; startBaseDefault = "0xe1E0DB951844E7fb727574D7dACa68d1C5D1525b"; startPairDefault = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; exchangeContract = web3.eth.contract(exchangeABI).at(exchangeAddr); wrapContract = web3.eth.contract(wrapABI).at(wrapAddr); setCookie("baseDefaultAddr", startBaseDefault, 10); setCookie("pairDefaultAddr", startPairDefault, 10); } else if (netId == 3) { ropsten = true; ropstenDomain = "ropsten."; printStatus(false, 'Ropsten Network Selected!'); exchangeAddr = "0xd85c6c8FAD288AB905747149799223Fe554686dD"; wrapAddr = "0xb603cEa165119701B58D56d10D2060fBFB3efad8"; startBaseDefault = "0x7Da44D4E0856b337f38e50b342237DC86D9032E3"; startPairDefault = "0x3d74C695B17505606480dae9EF3DCd6756C728DA"; exchangeContract = web3.eth.contract(exchangeABI).at(exchangeAddr); wrapContract = web3.eth.contract(wrapABI).at(wrapAddr); setCookie("baseDefaultAddr", startBaseDefault, 10); setCookie("pairDefaultAddr", startPairDefault, 10); } else { printStatus(false, 'Network not suported, select MainNet or Ropsten Network!'); ropsten = true; ropstenDomain = "notsuported."; exchangeAddr = "0xd85c6c8FAD288AB905747149799223Fe554686dD"; wrapAddr = "0xb603cEa165119701B58D56d10D2060fBFB3efad8"; startBaseDefault = "0x7Da44D4E0856b337f38e50b342237DC86D9032E3"; startPairDefault = "0x3d74C695B17505606480dae9EF3DCd6756C728DA"; exchangeContract = web3.eth.contract(exchangeABI).at(exchangeAddr); wrapContract = web3.eth.contract(wrapABI).at(wrapAddr); setCookie("baseDefaultAddr", startBaseDefault, 10); setCookie("pairDefaultAddr", startPairDefault, 10); } document.getElementById('accountExplorerLink').href = 'https://'+ropstenDomain+'etherscan.io/address/'+web3.eth.accounts[0]; account = web3.eth.accounts[0]; accountInterval = setInterval(function() { if (web3.eth.accounts[0] !== account) { account = web3.eth.accounts[0]; location.reload(); } }, 100); currentNetwork = netId; networkInterval = setInterval(function() { web3.version.getNetwork((err, netId) => { if (netId != currentNetwork) { currentNetwork = netId; location.reload(); } }); if ((asyncElmDetected > 0) && (asyncElmLoaded >= asyncElmDetected)) { asyncElmDetected = 0; asyncElmLoaded = 0; loading = false; sortOrders('typeSell',false); sortOrders('typeBuy',false); } }, 500); watchInterval = setInterval(function() { if (!loading) { orderWatch(); } }, 6000); document.getElementById('helpLink').href = 'about.html'; document.getElementById("myBaseBalance").value = 0; document.getElementById("myPairBalance").value = 0; getMyBalanceLabel(wrapAddr,web3.eth.accounts[0],'wrapBalance'); getEthBalanceLabel(web3.eth.accounts[0],'ethBalance'); listTokens("tkaddress"); getFees(); document.getElementById('contractExplorerLink').href = 'https://'+ropstenDomain+'etherscan.io/address/'+exchangeAddr; }); } window.addEventListener('load', async () => { resetLikes(); document.getElementById("btnSell").disabled = true; document.getElementById("btnSell").style.background = 'silver'; document.getElementById("btnBuy").disabled = true; document.getElementById("btnBuy").style.background = 'silver'; if (window.ethereum) { window.web3 = new Web3(ethereum); try { await ethereum.enable(); startEx(); } catch (err) { document.getElementById('statust1').innerText = 'User denied account access'; } } else if (window.web3) { window.web3 = new Web3(web3.currentProvider); startEx(); } else { document.getElementById('statust1').innerText = 'No Metamask (or other Web3 Provider) installed'; } })
Token Register
- Contract Side Solidity:
function registerToken(address _token) public payable { require((msg.sender == owner) || (msg.value >= registerFee), "Register Fee Very Low"); erc20 refToken = erc20(_token); if (!exists[_token]) { indexCount = indexCount+1; index[indexCount] = _token; tokens[_token].tokenBase = _token; tokens[_token].name = refToken.name(); tokens[_token].symbol = refToken.symbol(); tokens[_token].decimals = refToken.decimals(); tokens[_token].likesCount = 0; tokens[_token].dislikesCount = 0; tokens[_token].marketsCount = 0; exists[_token] = true; } }
1- address: Token to register;
2- refToken: Get data from token contract;
3- indexCount: Increase token count;
4- index[]: Define tokenCount as token index;
5- name, symbol and decimal: from refToken contract;
6- liskesCount, dislikes and marketsCount: Start with 0;
7- exists: Formal, always true;
- Web Side JS:
function sendRegisterToken() { token = document.getElementById('registerBaseCoin').value; exchangeContract.registerToken(token, {value:registerFee, gas:250000,}, (err, transactionId) => { printStatus(err, transactionId); }); }
Create Market
- Contract Side Solidity:
function createMarket(address _token, address _tokenPair) public payable { require(msg.value >= openMarketFee, "Open Market Fee Very Low"); require(exists[_token] && exists[_tokenPair],"token or tokenPair not listed"); require(!tokens[_token].markets[_tokenPair].exists,"Market already exists"); require(tokens[_token].tokenBase != _tokenPair,"Not allowed token = tokenPair"); tokens[_token].marketsCount = tokens[_token].marketsCount+1; tokens[_token].marketIndex[tokens[_token].marketsCount] = _tokenPair; tokens[_token].markets[_tokenPair].tokenPair = _tokenPair; tokens[_token].markets[_tokenPair].ordersCount = 0; tokens[_token].markets[_tokenPair].donesCount = 0; tokens[_token].markets[_tokenPair].exists = true; }
1- _token and _tokenPair: Pair market;
2- openMarketFee: Fee value for market creation;
3- marketsCount: Increase count of markets of base token;
4- marketIndex: Make marketsCount as index of current market;
5- ordersCount and donesCount: Set vars with 0;
6- exists: Formal always true after creation;
- Web Side JS:
function sendCreateMarket() { baseToken = document.getElementById('baseCoin').value; pairToken = document.getElementById('pairAddr').value; exchangeContract.createMarket(baseToken, pairToken, {value: marketFee, gas:200000,}, (err, transactionId) => { printStatus(err, transactionId); }); }