What’s new about crypto, betting and dApps?

AFFI. DEX affiliate program for Waves dApps by Bettex project

Decentralized affiliate program on Waves blockchain implemented as part of Waves Labs grant by Bettex project team.

Bettex project
7 min readJul 9, 2019
available on GitHub.com

This dApp for affiliate programs is a template for projects that include affiliate as part of its functionality. Code can be used as a template for copy or as a library or as a set of ideas for technical implementation.

It works like a regular affiliate-system that implements registration with the referrer, multi-level accrual of referral fees and motivation for registration in the system (i.e. cashback). It’s a pure dApp, web app interacts directly with blockchain without having its own backend, database, etc.

Here are techniques that can also be useful in many other projects:

1. Call a smart account in debt with immediate repayment (at the time of a call, there are no tokens on the account to pay for a call, but they appear there as a result of a call).

2. PoW-captcha — protection against high-frequency automated calling functions of a smart account. That’s an analogue of the captcha, but it works through proof of the use of computing resources.

3. Request for data keys by pattern.

dApp consists of:

- the smart account code in the ride4dapps language (according to the initial idea, it’s merged into the main smart account for which it will be necessary to implement affiliate functionality)

- js-wrappers that implement a level of abstraction over the WAVES NODE REST API

- code on the vuejs framework, which is an example of the use of the library and RIDE code.

So let’s look at all these details.

Call a smart account in debt with immediate repayment

Calling InvokeScript requires payment of fee from the account initiating the transaction. This isn’t a problem if you are doing a project for blockchain geeks who have some WAVES tokens in their account. But if the product is intended for use in the masses, then this becomes a serious problem. After all, user must attend to the purchase of Waves tokens (or another suitable asset), this is already a considerable threshold for entering the project. We can distribute asset to users, which it is possible to pay for transactions and then face the risk of their misuse, when automated systems are created for siphoning liquid assets from our system.

It would be very convenient if it is possible to make a call to InvokeScript “at the expense of the recipient” (the smart account on which the script is installed). And such a possibility exists, although this is not obvious.

If inside InvokeScript you make ScriptTransfer to the address of caller, who compensates for tokens spent on fee, then call will succeed, even if there were no assets at the time of call. This is possible due to the fact that checking the presence of a sufficient number of tokens is performed after calling the transaction, and not in front of it, so you can make a transaction on credit if this debt is immediately repaid.

ScriptTransfer(i.caller, i.fee, unit)

The code below refund the spent fee at the expense of smart account. To protect against misuse of this feature, you need to use a check that caller spends fee in the required asset and within reasonable limits:

func checkFee(i:Invocation) = {
if i.fee > maxFee then throw(“unreasonable large fee”) else
if i.feeAssetId != unit then throw(“fee must be in WAVES”) else true

}

Also to prevent from malicious and senseless waste of funds protection against automatic calling (PoW-captcha) is necessary.

PoW-captcha

The idea of ​​proof-of-work captcha is not new and has already been implemented in various projects, including those based on WAVES. The idea is that in order to commit an action that spends the resources of our project, the caller must spend his own resources, which makes the attack on the depletion of resources quite costly. For very easy and low-cost validation of the fact that the sender of the transaction has solved the PoW task, there is a transaction id check:

if take(toBase58String(i.transactionId), 3) != “123” then throw(“proof of work failed”) else

In order to make a transaction the caller must choose such parameters so that its base58 code (id) starts at 123, which corresponds to an average of a couple of tens of seconds of processor time, which is generally reasonable for our task. If a simpler or more complex PoW is required, then task is easy to modify in an obvious way.

Request for data keys by pattern

In order to use blockchain as a database you must have API tools for querying the database as a key-val for patterns.

Such a toolkit appeared in early July 2019 as ?matches parameter of the REST API request /addresses/data?matches=regexp. Now, if we need to get not one key from the web application and not all keys at once, but only some group, then we can make a selection by the name of the key. For example in this project, withdrawal transactions are encoded as:

withdraw_${userAddress}_${txid}

This allows you to get a list of withdrawal transactions for any given address using a pattern:

?matches=withdraw_${userAddress}_.*

Now let’s look at the components of the finished solution.

Vuejs code

The code is a working demonstration, close to the real project. He realizes logging in through Waves Keeper and working with the affiliate.js library, with which he registers the user in the system, polls transaction data, and also allows you to withdraw the funds earned to the user account.

RIDE code

It consists of the register, fund and withdraw functions.

The register function registers a user in the system. It has two parameters: referer (the address of the referral) and the salt parameter that is not used in the function’s code, which is needed for matching the transaction id (the PoW-captcha task).

This function (like the others of this project) uses the “call-in-debt” technique, the result of the function is the financing of the payment of a fee for calling this function. With this solution, the user creates a wallet and can immediately work with the system, and he does not need to bother with acquiring or receiving an asset allowing him to pay a transaction fee.

The result of the registration function are two entries:

${owner)_referer = referer
${referer}_referral_${owner} = owner

This allows direct and reverse searches (the referrer of this user and all referrals of this user).

The fund function is rather a template for developing real functionality. In this form, it takes all the funds transferred by the transaction and distributes them to the referrer accounts 1, 2, 3 levels, to cashback account and change account (everything that remains in the distribution of the previous accounts gets here)

Cashback is a means of encouraging the end user to participate in the referral system. Part of the fee, which the system pays as a cashback user can withdraw in the same way as rewards for referrals.

When using the referral system, the fund function should be modified, embedded in the main logic of the smart account on which the system will operate. For example, if a referral reward is paid for a bet made, then the fund function must be built into the logic where the bet is made (or another target action is taken for which the reward is paid). There are three levels of referral rewards. If you want to make more or less levels, you can edit it in the code. The percentage of reward is given by level 1…level 3 constants. This is calculated in the code as amount*level/1000, the value 1 corresponds to 0.1% (this can also be changed in the code).

The function call changes the account balance and also creates records for logging purposes in the form:

fund_address_txid = address:owner:inc:level:timestamp

For receiving timestamp (current time) the sheaf is used:

func getTimestamp() = {
let block = extract(blockInfoByHeight(height))
toString(block.timestamp)

}

That is, the transaction time is the time of the block in which it is located. This is safer than using timestamp from the transaction itself, especially since it is not accessible from callable.

The withdraw function makes a withdrawal of all accrued rewards to a user account. This creates entries for logging purposes:

# withdraw log: withdraw_user_txid=amount:timestamp

Appendix

The main part of this dApp is the affiliate.js library, which is a link between the affiliate data models and the WAVES NODE REST API. This implements a level of abstraction independent of the framework (any one can be used). Active functions (register, withdraw) assume that waves keeper is installed on the system, the library itself does not check this.
It implements the methods:

fetchReferralTransactions
fetchWithdrawTransactions
fetchMyBalance
fetchReferrals
fetchReferer
withdraw
register

The functionality of these methods is obvious from the names, parameters and the returned data are described in the code. Additional comments are required by the register function — it initiates a cycle for selecting an id transaction so that it starts with 123 — this is the PoW-captcha described above, which protects against mass registrations. The function first finds the transaction with the desired id, and then signs it through Waves Keeper.

DEX affiliate program is available on GitHub.com

--

--

Bettex project

Our key idea is building decentralized betting exchange based on Waves blockchain. www.bettex.bet