Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WavesDAO -> WXDAO funding #446

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/wxdao_funding/wxdao_funding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# WavesDAO -> WXDAO Funding

## Keys
| key | type | value |
| :---------------------------------- | --------: | :----------------------------------------------------------- |
| `%s__mainTreasuryAddress` | `String` | `'3PEwRcYNAUtoFvKpBhKoiwajnZfdoDR6h4h'` |
| `%s__WavesUSDTPoolAddress` | `String` | `'3PKfrupEydU2nZAghVjZAfvCwMBkzuR1F52'` |
| `%s__WXDAOcontractAddress` | `String` | `'3PEhMFF2mfSWVxWqbW8guXVYcNeoW77ga7T'` |
| `%s__WXDAOassetId` | `String` | `'BE4VVq1VsrwGyUWpUkNjVFR5j9vzioiRhrUT52p8RW2m'` |
| `%s__USDTassetId` | `String` | `'G5WWWzzVsWRyzGf32xojbnfp7gXbWrgqJT8RcVWEfLmC'` |
| `%s__minClaimAmount` | `Integer` | `30000000000` |
| `%s__processFeeAmount` | `Integer` | `500000` |
| `%s__WXDAOpriceCoeff` | `Integer` | `110000000` (SCALE8) |
| `%s%s%s__history__<action>__<txId>` | `String` | `'%d%d%d__<wavesAmount>__<WXDAOamount>__<processFeeAmount>'` |


# Functions


## Process
- Waves amount claimed from WavesDAO should be exceed `minClaimAmount` (*default: 300.0*)
- `processFee` (*default: 0.005*) Waves amount is sent back to caller
```
@Callable(i)
func process()
```
48 changes: 48 additions & 0 deletions migrations/2024_03_29_wxdao_funding/01_wxdao_funding_data_tx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"type": 12,
"fee": 900000,
"version": 2,
"senderPublicKey": "5WG53hB4ZK2TWX7UnLeTU4ieDp8Fy4VtaYx6gKFYxGHu",
"data": [
{
"key": "%s__mainTreasuryAddress",
"type": "string",
"value": "3PEwRcYNAUtoFvKpBhKoiwajnZfdoDR6h4h"
},
{
"key": "%s__WavesUSDTPoolAddress",
"type": "string",
"value": "3PKfrupEydU2nZAghVjZAfvCwMBkzuR1F52"
},
{
"key": "%s__WXDAOcontractAddress",
"type": "string",
"value": "3PEhMFF2mfSWVxWqbW8guXVYcNeoW77ga7T"
},
{
"key": "%s__WXDAOassetId",
"type": "string",
"value": "BE4VVq1VsrwGyUWpUkNjVFR5j9vzioiRhrUT52p8RW2m"
},
{
"key": "%s__USDTassetId",
"type": "string",
"value": "G5WWWzzVsWRyzGf32xojbnfp7gXbWrgqJT8RcVWEfLmC"
},
{
"key": "%s__minClaimAmount",
"type": "integer",
"value": 30000000000
},
{
"key": "%s__processFeeAmount",
"type": "integer",
"value": 500000
},
{
"key": "%s__WXDAOpriceCoeff",
"type": "integer",
"value": 110000000
}
]
}
195 changes: 195 additions & 0 deletions ride/wxdao_funding.ride
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}

let SEP = "__"
let CONTRACT_NAME = "wxdao_funding.ride"
let SCALE8 = 100_000_000
let WAVES = "WAVES"

func wrapErr(s: String) = {
CONTRACT_NAME + ": " + s
}

func throwErr(s: String) = {
throw(wrapErr(s))
}

func keyMainTreasuryAddress() = ["%s", "mainTreasuryAddress"].makeString(SEP)
func keyWavesUSDTPoolAddress() = ["%s", "WavesUSDTPoolAddress"].makeString(SEP)
func keyWXDAOAddress() = ["%s", "WXDAOcontractAddress"].makeString(SEP)
func keyWXDAOassetId() = ["%s", "WXDAOassetId"].makeString(SEP)
func keyUSDTassetId() = ["%s", "USDTassetId"].makeString(SEP)
func keyProcessFeeAmount() = ["%s", "processFeeAmount"].makeString(SEP)
func keyMinClaimAmount() = ["%s", "minClaimAmount"].makeString(SEP)
func keyWXDAOpriceCoeff() = ["%s", "WXDAOpriceCoeff"].makeString(SEP)

func keyHistory(action: String, txId: String) = ["%s%s%s", "history", action, txId].makeString(SEP)

func formatProcessHistory(claimedWavesAmount: Int, sendWXDAOamount: Int, processFeeAmount: Int) = {
[
"%d%d%d",
claimedWavesAmount.toString(),
sendWXDAOamount.toString(),
processFeeAmount.toString()
].makeString(SEP)
}

let mainTreasuryAddressString = this.getStringValue(keyMainTreasuryAddress())
let mainTreasuryAddressOrFail = mainTreasuryAddressString.addressFromStringValue()
let wavesUSDTpoolAddressString = this.getStringValue(keyWavesUSDTPoolAddress())
let wavesUSDTpoolAddressOrFail = wavesUSDTpoolAddressString.addressFromStringValue()
let wxdaoAddressString = this.getStringValue(keyWXDAOAddress())
let wxdaoAddressOrFail = wxdaoAddressString.addressFromStringValue()

func stringToAsset(assetIdString: String) = {
if (assetIdString == WAVES) then unit else assetIdString.fromBase58String()
}

func assetToString(assetId: ByteVector|Unit) = {
match (assetId) {
case b:ByteVector => b.toBase58String()
case _ => WAVES
}
}

func getBalance(address: Address, assetIdString: String) = {
let assetId = stringToAsset(assetIdString)

match (assetId) {
case b:ByteVector => address.assetBalance(b)
case _ => address.wavesBalance().available
}
}

let processFeeAmount = this.getInteger(keyProcessFeeAmount()).valueOrElse(500000)
let minClaimAmount = this.getInteger(keyMinClaimAmount()).valueOrElse(0)
let wxdaoPriceCoeff = this.getInteger(keyWXDAOpriceCoeff()).valueOrElse(SCALE8)
let usdtAssetIdString = this.getStringValue(keyUSDTassetId())
let wxdaoAssetIdString = this.getStringValue(keyWXDAOassetId())
let wxdaoAssetId = stringToAsset(wxdaoAssetIdString)


##### PROPOSAL VERIFIER #####
func keyVotingResultAddress() = "contract_voting_result"
func keyProposalAllowBroadcast(address: Address, txId: ByteVector) =
((("proposal_allow_broadcast_" + toString(address)) + "_") + toBase58String(txId))

let votingResultAddress = match getString(this, keyVotingResultAddress()) {
case s: String =>
addressFromString(s)
case _: Unit =>
unit
case _ =>
throw("Match error")
}
##### PROPOSAL VERIFIER #####

func claimWavesFromTreasury() = {
strict oldWavesBalance = this.getBalance(WAVES)
strict getWavesInvoke = mainTreasuryAddressOrFail.invoke("Claim", [], [])
strict newWavesBalance = this.getBalance(WAVES)

newWavesBalance - oldWavesBalance
}

func getPoolBalance(poolAddress: Address, assetIdString: String) = {
let invokeResult = poolAddress.invoke("getAccBalanceWrapperREADONLY", [assetIdString], [])
match (invokeResult) {
case balance:Int => balance
case _ => "getAccBalanceWrapperREADONLY unexpected value".throwErr()
}
}

func getWavesUSDTPrice() = {
let poolWavesBalance = wavesUSDTpoolAddressOrFail.getPoolBalance(WAVES)
let poolUsdtBalance = wavesUSDTpoolAddressOrFail.getPoolBalance(usdtAssetIdString)

strict ch = [
poolWavesBalance > 0 || "WAVES/USDT pool Waves balance should be greater that 0".throwErr(),
poolUsdtBalance > 0 || "WAVES/USDT pool USDT balance should be greater that 0".throwErr()
]

fraction(poolUsdtBalance, SCALE8, poolWavesBalance)
}

func getWXDAOUsdtPrice() = {
let priceInvoke = wxdaoAddressOrFail.invoke("call", ["price", []], [])
let invokeResult = match (priceInvoke) {
case r:List[Any] => {
match(r[0]) {
case i:Int => fraction(i, wxdaoPriceCoeff, SCALE8)
case _ => unit
}
}
case _ => unit
}
let wxDAOprice = invokeResult.valueOrErrorMessage("Unexpected WXDAO Price invoke result".wrapErr())

strict ch = [
wxDAOprice > 0 || "WXDAO price should be greater than 0".throwErr()
]

wxDAOprice
}

func getWavesWXDAOPrice() = {
let wavesUsdtPrice = getWavesUSDTPrice()
let wxdaoUsdtPrice = getWXDAOUsdtPrice()

fraction(wavesUsdtPrice, SCALE8, wxdaoUsdtPrice)
}

func calcWXDAOamount(wavesAmount: Int) = {
let price = getWavesWXDAOPrice()
let wxDAOamount = fraction(wavesAmount, price, SCALE8)

strict ch =[
wavesAmount > 0 || "wavesAmount should be greater than 0".throwErr(),
price > 0 || "price should be greater than 0".throwErr(),
wxDAOamount > 0 || "wxDAO swap amount is 0".throwErr()
]

wxDAOamount
}

@Callable(i)
func process() = {
strict claimedWavesAmount = claimWavesFromTreasury()
strict swapWavesAmount = claimedWavesAmount - processFeeAmount
strict wxDAOsendAmount = calcWXDAOamount(swapWavesAmount)

strict ch = [
claimedWavesAmount >= minClaimAmount ||
["not enough claim amount (", claimedWavesAmount.toString(), " < ", minClaimAmount.toString(), ")"].makeString("").throwErr(),
swapWavesAmount > 0 || "claimed waves amount should be greater than processing fee".throwErr(),
wxDAOsendAmount > 0 || "WXDAO send amount should be greater than 0".throwErr()
]

let processingCaller = i.originCaller

[
ScriptTransfer(processingCaller, processFeeAmount, stringToAsset(WAVES)),
ScriptTransfer(mainTreasuryAddressOrFail, wxDAOsendAmount, wxdaoAssetId),
StringEntry(
keyHistory("process", i.transactionId.toBase58String()),
formatProcessHistory(claimedWavesAmount, wxDAOsendAmount, processFeeAmount)
)
]
}


@Verifier(tx)
func verify () = {
let byProposal = match votingResultAddress {
case proposalAddress: Address =>
valueOrElse(getBoolean(proposalAddress, keyProposalAllowBroadcast(this, tx.id)), false)
case _ =>
false
}

let byOwner = sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
if (byProposal)
then true
else byOwner
}
101 changes: 101 additions & 0 deletions test/components/wxdao_funding/_hooks.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { address, randomSeed, publicKey } from '@waves/ts-lib-crypto';
import {
data,
massTransfer,
issue,
} from '@waves/waves-transactions';
import { format } from 'path';
import { setScriptFromFile } from '../../utils/utils.mjs';

import {
broadcastAndWait, chainId, baseSeed,
} from '../../utils/api.mjs';

const seedWordsCount = 5;
const ridePath = '../ride';
const mockPath = 'components/wxdao_funding/mock';
const wxdaoFundingPath = format({ dir: ridePath, base: 'wxdao_funding.ride' });
const wxdaoMockPath = format({ dir: mockPath, base: 'wxdao.mock.ride' });
const poolMockPath = format({ dir: mockPath, base: 'pool.mock.ride' });
const mainTreasury = format({ dir: mockPath, base: 'mainTreasury.mock.ride' });

export const mochaHooks = {
async beforeAll() {
const names = [
'wxdaoFunding',
'wxdao',
'mainTreasury',
'wavesUsdtPool',
'user1',
];
this.accounts = Object.fromEntries(names.map((item) => {
const itemSeed = randomSeed(seedWordsCount);
return [
item,
{ seed: itemSeed, addr: address(itemSeed, chainId), publicKey: publicKey(itemSeed) },
];
}));
const amount = 100e8;
const massTransferTx = massTransfer({
transfers: Object.values(this.accounts).map((item) => ({ recipient: item.addr, amount })),
chainId,
}, baseSeed);
await broadcastAndWait(massTransferTx);

this.wxdaoAssetId = await broadcastAndWait(issue({
quantity: 1e6 * 1e8,
decimals: 8,
name: 'WXDAO',
description: 'WXDAO',
chainId,
}, baseSeed)).then((tx) => tx.id);

await broadcastAndWait(massTransfer({
transfers: Object.values(this.accounts).map((item) => ({ recipient: item.addr, amount })),
chainId,
assetId: this.wxdaoAssetId,
}, baseSeed));

await broadcastAndWait(data({
additionalFee: 4e5,
data: [
{
key: '%s__mainTreasuryAddress',
type: 'string',
value: this.accounts.mainTreasury.addr,
},
{
key: '%s__WavesUSDTPoolAddress',
type: 'string',
value: this.accounts.wavesUsdtPool.addr,
},
{
key: '%s__WXDAOcontractAddress',
type: 'string',
value: this.accounts.wxdao.addr,
},
{
key: '%s__WXDAOassetId',
type: 'string',
value: this.wxdaoAssetId,
},
{
key: '%s__USDTassetId',
type: 'string',
value: 'MOCKED_USDT_ASSET_ID',
},
{
key: '%s__WXDAOpriceCoeff',
type: 'integer',
value: 1_1000_0000, // 110%
},
],
chainId,
}, this.accounts.wxdaoFunding.seed));

await setScriptFromFile(wxdaoFundingPath, this.accounts.wxdaoFunding.seed);
await setScriptFromFile(wxdaoMockPath, this.accounts.wxdao.seed);
await setScriptFromFile(poolMockPath, this.accounts.wavesUsdtPool.seed);
await setScriptFromFile(mainTreasury, this.accounts.mainTreasury.seed);
},
};
11 changes: 11 additions & 0 deletions test/components/wxdao_funding/mock/mainTreasury.mock.ride
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}

# Send 1.5 WAVES
@Callable(i)
func Claim() = {
[
ScriptTransfer(i.caller, 1_5000_0000, unit)
]
}
Loading
Loading