diff --git a/ride4dapps/mixer/README.md b/ride4dapps/mixer/README.md new file mode 100644 index 0000000..a641a8c --- /dev/null +++ b/ride4dapps/mixer/README.md @@ -0,0 +1,14 @@ +# R4D Mixer +This is an example of the crypto coins mixer + +1. A user can send money with an arbitrary Blake2b256 hash +2. After that, anyone with address matching that hash and knowing how much bytes of address used can take the money back + +Try now: https://mixer.waves.today (Requires Waves Keeper with InvokeScript support **in TESTNET mode**) + +# Example +1. Taken destination address is `3Musn2zFuw3G71yg6G7PDRAq7CjWxK7Z4pk` and complexity is 3, caller takes first 3 and last 3 bytes of it: `3Mu + 4pk`, hashes it with Blake2b256 and invokes `deposit(hash)` with attached Waves payment +2. Then owner of `3Musn2zFuw3G71yg6G7PDRAq7CjWxK7Z4pk` invokes `withdraw(3)`, script compares his address with stored hash and withdraws all money associated with it +3. You can tune complexity from 1 to 9 + 1. With low complexity everyone can generate address that matches the resulting hash, but then no one can prove that you took the money + 2. So with complexity = 9 address should match exactly, and it's safe but there is no [plausible deniability](https://en.wikipedia.org/wiki/Plausible_deniability) diff --git a/ride4dapps/mixer/mixer.ride b/ride4dapps/mixer/mixer.ride new file mode 100644 index 0000000..03ddda7 --- /dev/null +++ b/ride4dapps/mixer/mixer.ride @@ -0,0 +1,36 @@ +{-# STDLIB_VERSION 3 #-} +{-# CONTENT_TYPE DAPP #-} +{-# SCRIPT_TYPE ACCOUNT #-} + +let FeePercent = 1 + +@Callable(i) +func deposit(hash: ByteVector) = { + let pmt = extract(i.payment) + if (isDefined(pmt.assetId) || pmt.amount < 100) then throw("can hold waves only at the moment") + else { + let currentKey = toBase58String(hash) + let currentAmount = match getInteger(this, currentKey) { + case a:Int => a + case _ => 0 + } + let fee = pmt.amount * FeePercent / 100 + let newAmount = currentAmount + pmt.amount - fee + WriteSet([DataEntry(currentKey, newAmount)]) + } +} + +@Callable(i) +func withdraw(complexity: Int) = { + let currentKey = toBase58String(blake2b256(take(drop(i.caller.bytes, 2), complexity) + takeRight(i.caller.bytes, complexity))) + let amount = match getInteger(this, currentKey) { + case a:Int => a + case _ => 0 + } + if (amount <= 0) + then throw("Can't withdraw negative amount") + else ScriptResult( + WriteSet([DataEntry(currentKey, 0)]), + TransferSet([ScriptTransfer(i.caller, amount, unit)]) + ) +} diff --git a/ride4dapps/mixer/mixer_test.js b/ride4dapps/mixer/mixer_test.js new file mode 100644 index 0000000..e2c0e1a --- /dev/null +++ b/ride4dapps/mixer/mixer_test.js @@ -0,0 +1,12 @@ +describe('Mixer test', () => { + const dappAddress = "3MwmaLMtWTaTyjhzywLLRQa4BH1PYZdvpKy" + + const hash = "8N5gCoRKcCD4BxJNGn2kKUzMBieetEh4r7DUMHa1m2gq" + + it('Deposit funds', async function(){ + const tx = invokeScript({ fee: 900000, dApp: dappAddress, call:{function:"deposit",args:[{"type": "binary", "value": hash}]}, + payment: [{amount: 1000000, assetId: null}]}) + await broadcast(tx) + await waitForTx(tx.id) + }) + })