Skip to content

Commit d110ef7

Browse files
authored
Merge pull request #107 from aus87/main
XRDao
2 parents 765ac16 + c17df6c commit d110ef7

File tree

8 files changed

+2218
-0
lines changed

8 files changed

+2218
-0
lines changed

5-DAO/xrdao/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "xrdao"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
sbor = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.5.0" }
8+
scrypto = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.5.0" }
9+
10+
[dev-dependencies]
11+
transaction = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.5.0" }
12+
radix-engine = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.5.0" }
13+
scrypto-unit = { git = "https://github.com/radixdlt/radixdlt-scrypto", tag = "v0.5.0" }
14+
15+
[profile.release]
16+
opt-level = 's' # Optimize for size.
17+
lto = true # Enable Link Time Optimization.
18+
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
19+
panic = 'abort' # Abort on panic.
20+
strip = "debuginfo" # Strip debug info.
21+
overflow-checks = true # Panic in the case of an overflow.
22+
23+
[lib]
24+
crate-type = ["cdylib", "lib"]

5-DAO/xrdao/src/lib.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/// **This submission does not need to be judged due to being incomplete***
2+
///
3+
/// XRDao protocol is built for medium to long term XRD holders and XRD altcoin
4+
/// investors that are interested in a sustainable rewards structure. XRDao is the main
5+
/// component where most of the interactions take place. This blueprint allows users to mint
6+
/// XRDao which is pegged 1:1 to XRD. The XRDao token has a small tax taken for every trade,
7+
/// transfer, mint, or burn starting at .5% and can change from governance vote. A percent of platform
8+
/// fees (which also can change via governanve vote) go towards buying protocol owned liquidity from the
9+
/// radiswap component. The rest of platform fees collected are distributed to users of XRDao
10+
/// that make successful trades and strategies when investing through the platform. Future investment
11+
/// choices will be added along with altcoin investing such as staking to trustworthy validators, loaning
12+
/// funds to a lending platform for interest. In order to use XRDao, a user would send XRD
13+
/// to the platform and mint XRDao token. The exchange rate for minting and burning XRDao always remains
14+
/// 1:1 minus the buy/sell fee. The user would then use these XRDao tokens to pick investment options
15+
/// just as they would with XRD, but the platform would be making the actual trades/stakes for you.
16+
/// When a user makes a successful trade, Repuation (rep) tokens are awarded for the amount of xrd earned.
17+
///
18+
/// For example:
19+
/// User 1 has 100 xrd and mints 99.5 xrdao,
20+
/// User 1 invests 99.5 xrdao into oci,
21+
/// Oci goes 2x up in price relative to xrd,
22+
/// User 1 exits oci position
23+
/// User 1 sends xrdao to burn in return for xrd
24+
///
25+
/// User 1 now has 198.005 xrdao tokens that can be burned and exchanged for xrd minus the burn fee, as
26+
/// well as 1.495 Rep. Rep is a soulbound token created by the XRDao platform to signify who is most profitable
27+
/// for the platform. It can't be bought, sold, or traded, but it can be earned and minted for good behavior
28+
/// or removed and burned for bad behavior. Users are catagorized into Rank by the amount of rep they hold
29+
/// into the following ranks:
30+
///
31+
/// President, Highest rep, linear relationship between xrdao and voting power, top 1% of users, 3x reward multiplier
32+
/// VicePresident, x = y^1.1 voting power, top 10% of users, 1.67x reward multiplier
33+
/// Senator, x = y^1.2 voting power, top 30% of users, 1.25x reward multiplier
34+
/// Governor, x = y^1.3 voting power, top 50% of users, 1x reward multiplier
35+
/// Citizen, x = y^1.5 voting power, top 75% of users, .925x reward multiplier
36+
/// Pleb, Lowest rep, quadratic relationship, x = y^2 voting power, top 100% of users, 0x reward multiplier
37+
///
38+
/// As you can see, different ranks give different curves to determine voting power and different shares of the
39+
/// fees generated from the platform. The more money you make, the more rep you earn, the more fees you acrue.
40+
/// You might be asking yourself, why wouldnt these users just invest themselves and avoid platform fees
41+
/// alltogether? XRDao harneses sustainable revenues from market volatility. When the protocol buys liquidity
42+
/// from radiswap for XRDao-XRD, XRD volatility will generate price differences between the two tokens in the pool.
43+
/// When there is ever a price difference above the fee that is being charged (.5%) there will be a profitable
44+
/// arbitration opportunity within the pool. Whether an arbitrage bot or a user makes it, fees are then
45+
/// generated from that trade and go back to buying more liquidity and rewarding each user based on reputation.
46+
/// Reputation is basically earning a share of xrd market volatility combined with platform fees.
47+
///
48+
/// The majority of my work here is to the xrdaouser and xrdao components. Tokentax is a start of an attempt to
49+
/// design a component that a token must pass through in order to extract taxes from dex transactions. My
50+
/// understanding is this feature doesnt current work at the resource level, but hopefully it will in the
51+
/// future. Radiswap is instantiated to trade and provide liquidity for XRDao-XRD token.
52+
///
53+
///
54+
/// XRDaoProposal is a copy paste from the Liquidity DAO scrypto examples that I am in process of modifying to fit
55+
/// my own purposes. This component is for creating voting proposals that must be funded and voted on. Vote weight is calculated by
56+
/// total xrdao tokens held on the platform and the curve associated with the user's Rank. Proposals can be challenge
57+
/// any proposal they deem harmful to the platform by voting with reputation instead of xrdao token balance.
58+
/// When voting with rep, you stake your rep tokens on the correct outcome of the proposal. Those on the losing side
59+
/// of the vote forfeit their tokens to the winning side. This way the voters with the most conviction on the best
60+
/// outcome will be willing to risk their earning power from the platform on the outcome.
61+
///
62+
/// written by Austin
63+
/// @aus877 on twitter
64+
/// @aus87 on telegram
65+
66+
67+
mod radiswap;
68+
mod xrdao;
69+
mod xrdaousers;
70+
mod xrdaoproposal;
71+
mod tokentax;

5-DAO/xrdao/src/radiswap.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use scrypto::prelude::*;
2+
3+
blueprint! {
4+
struct Radiswap {
5+
/// The resource address of LP token.
6+
lp_resource_address: ResourceAddress,
7+
/// LP tokens mint badge.
8+
lp_mint_badge: Vault,
9+
/// The reserve for token A.
10+
a_pool: Vault,
11+
/// The reserve for token B.
12+
b_pool: Vault,
13+
/// The fee to apply for every swap
14+
fee: Decimal,
15+
/// The standard (Uniswap-like) DEX follows the X*Y=K rule. Since we enable a user defined 'lp_initial_supply', we need to store this value to recover incase all liquidity is removed from the system.
16+
/// Adding and removing liquidity does not change this ratio, this ratio is only changed upon swaps.
17+
lp_per_asset_ratio: Decimal,
18+
}
19+
20+
impl Radiswap {
21+
/// Creates a Radiswap component for token pair A/B and returns the component address
22+
/// along with the initial LP tokens.
23+
pub fn instantiate_pool(
24+
a_tokens: Bucket,
25+
b_tokens: Bucket,
26+
lp_initial_supply: Decimal,
27+
lp_symbol: String,
28+
lp_name: String,
29+
lp_url: String,
30+
fee: Decimal,
31+
) -> (ComponentAddress, Bucket) {
32+
// Check arguments
33+
assert!(
34+
!a_tokens.is_empty() && !b_tokens.is_empty(),
35+
"You must pass in an initial supply of each token"
36+
);
37+
assert!(
38+
fee >= dec!("0") && fee <= dec!("1"),
39+
"Invalid fee in thousandths"
40+
);
41+
42+
// Instantiate our LP token and mint an initial supply of them
43+
let lp_mint_badge = ResourceBuilder::new_fungible()
44+
.divisibility(DIVISIBILITY_NONE)
45+
.metadata("name", "LP Token Mint Auth")
46+
.initial_supply(1);
47+
let lp_resource_address = ResourceBuilder::new_fungible()
48+
.divisibility(DIVISIBILITY_MAXIMUM)
49+
.metadata("symbol", lp_symbol)
50+
.metadata("name", lp_name)
51+
.metadata("url", lp_url)
52+
.mintable(rule!(require(lp_mint_badge.resource_address())), LOCKED)
53+
.burnable(rule!(require(lp_mint_badge.resource_address())), LOCKED)
54+
.no_initial_supply();
55+
56+
let lp_tokens = lp_mint_badge.authorize(|| {
57+
borrow_resource_manager!(lp_resource_address).mint(lp_initial_supply)
58+
});
59+
60+
// ratio = initial supply / (x * y) = initial supply / k
61+
let lp_per_asset_ratio = lp_initial_supply / (a_tokens.amount() * b_tokens.amount());
62+
63+
// Instantiate our Radiswap component
64+
let radiswap = Self {
65+
lp_resource_address,
66+
lp_mint_badge: Vault::with_bucket(lp_mint_badge),
67+
a_pool: Vault::with_bucket(a_tokens),
68+
b_pool: Vault::with_bucket(b_tokens),
69+
fee,
70+
lp_per_asset_ratio,
71+
}
72+
.instantiate()
73+
.globalize();
74+
75+
// Return the new Radiswap component, as well as the initial supply of LP tokens
76+
(radiswap, lp_tokens)
77+
}
78+
79+
/// Adds liquidity to this pool and return the LP tokens representing pool shares
80+
/// along with any remainder.
81+
pub fn add_liquidity(&mut self, mut a_tokens: Bucket, mut b_tokens: Bucket) -> (Bucket, Bucket) {
82+
// Get the resource manager of the lp tokens
83+
let lp_resource_manager = borrow_resource_manager!(self.lp_resource_address);
84+
85+
// Differentiate LP calculation based on whether pool is empty or not.
86+
let (supply_to_mint, remainder) = if lp_resource_manager.total_supply() == 0.into() {
87+
// Set initial LP tokens based on previous LP per K ratio.
88+
let supply_to_mint =
89+
self.lp_per_asset_ratio * a_tokens.amount() * b_tokens.amount();
90+
self.a_pool.put(a_tokens.take(a_tokens.amount()));
91+
self.b_pool.put(b_tokens);
92+
(supply_to_mint, a_tokens)
93+
} else {
94+
// The ratio of added liquidity in existing liquidty.
95+
let a_ratio = a_tokens.amount() / self.a_pool.amount();
96+
let b_ratio = b_tokens.amount() / self.b_pool.amount();
97+
98+
let (actual_ratio, remainder) = if a_ratio <= b_ratio {
99+
// We will claim all input token A's, and only the correct amount of token B
100+
self.a_pool.put(a_tokens);
101+
self.b_pool
102+
.put(b_tokens.take(self.b_pool.amount() * a_ratio));
103+
(a_ratio, b_tokens)
104+
} else {
105+
// We will claim all input token B's, and only the correct amount of token A
106+
self.b_pool.put(b_tokens);
107+
self.a_pool
108+
.put(a_tokens.take(self.a_pool.amount() * b_ratio));
109+
(b_ratio, a_tokens)
110+
};
111+
(
112+
lp_resource_manager.total_supply() * actual_ratio,
113+
remainder,
114+
)
115+
};
116+
117+
// Mint LP tokens according to the share the provider is contributing
118+
let lp_tokens = self.lp_mint_badge.authorize(|| {
119+
lp_resource_manager.mint(supply_to_mint)
120+
});
121+
122+
// Return the LP tokens along with any remainder
123+
(lp_tokens, remainder)
124+
}
125+
126+
/// Removes liquidity from this pool.
127+
pub fn remove_liquidity(&mut self, lp_tokens: Bucket) -> (Bucket, Bucket) {
128+
assert!(
129+
self.lp_resource_address == lp_tokens.resource_address(),
130+
"Wrong token type passed in"
131+
);
132+
133+
// Get the resource manager of the lp tokens
134+
let lp_resource_manager = borrow_resource_manager!(self.lp_resource_address);
135+
136+
// Calculate the share based on the input LP tokens.
137+
let share = lp_tokens.amount() / lp_resource_manager.total_supply();
138+
139+
// Withdraw the correct amounts of tokens A and B from reserves
140+
let a_withdrawn = self.a_pool.take(self.a_pool.amount() * share);
141+
let b_withdrawn = self.b_pool.take(self.b_pool.amount() * share);
142+
143+
// Burn the LP tokens received
144+
self.lp_mint_badge.authorize(|| {
145+
lp_tokens.burn();
146+
});
147+
148+
// Return the withdrawn tokens
149+
(a_withdrawn, b_withdrawn)
150+
}
151+
152+
/// Swaps token A for B, or vice versa.
153+
pub fn swap(&mut self, input_tokens: Bucket) -> Bucket {
154+
// Get the resource manager of the lp tokens
155+
let lp_resource_manager = borrow_resource_manager!(self.lp_resource_address);
156+
157+
// Calculate the swap fee
158+
let fee_amount = input_tokens.amount() * self.fee;
159+
160+
let output_tokens = if input_tokens.resource_address() == self.a_pool.resource_address() {
161+
// Calculate how much of token B we will return
162+
let b_amount = self.b_pool.amount()
163+
- self.a_pool.amount() * self.b_pool.amount()
164+
/ (input_tokens.amount() - fee_amount + self.a_pool.amount());
165+
166+
// Put the input tokens into our pool
167+
self.a_pool.put(input_tokens);
168+
169+
// Return the tokens owed
170+
self.b_pool.take(b_amount)
171+
} else {
172+
// Calculate how much of token A we will return
173+
let a_amount = self.a_pool.amount()
174+
- self.a_pool.amount() * self.b_pool.amount()
175+
/ (input_tokens.amount() - fee_amount + self.b_pool.amount());
176+
177+
// Put the input tokens into our pool
178+
self.b_pool.put(input_tokens);
179+
180+
// Return the tokens owed
181+
self.a_pool.take(a_amount)
182+
};
183+
184+
// Accrued fees change the raio
185+
self.lp_per_asset_ratio =
186+
lp_resource_manager.total_supply() / (self.a_pool.amount() * self.b_pool.amount());
187+
188+
output_tokens
189+
}
190+
191+
/// Returns the resource addresses of the pair.
192+
pub fn get_pair(&self) -> (ResourceAddress, ResourceAddress) {
193+
(
194+
self.a_pool.resource_address(),
195+
self.b_pool.resource_address(),
196+
)
197+
}
198+
}
199+
}

5-DAO/xrdao/src/tokentax.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/// **This submission does not need to be judged due to being incomplete***
2+
///
3+
///
4+
use scrypto::prelude::*;
5+
6+
blueprint! {
7+
struct TokenTax {
8+
/// The resource address of token to be taxed
9+
resource_address: ResourceAddress,
10+
tax: Decimal,
11+
tax_vault: Vault,
12+
}
13+
14+
impl TokenTax {
15+
/// Creates a Radiswap component for token pair A/B and returns the component address
16+
/// along with the initial LP tokens.
17+
pub fn new_token_tax(resource_address: ResourceAddress, tax: Decimal, tax_vault: Vault) -> (ComponentAddress, Bucket) {
18+
19+
// Check arguments
20+
assert!(tax >= dec!("0") && fee <= dec!("1"), "Invalid fee decimal");
21+
22+
let admin_badge = ResourceBuilder::new_fungible()
23+
.divisibility(DIVISIBILITY_NONE)
24+
.metadata("name", "TokenTax Admin")
25+
.initial_supply(1);
26+
27+
// Instantiate our Radiswap component
28+
let component = Self {
29+
admin_badge: admin_badge.resource_address(),
30+
admin_vault: Vault::with_bucket(admin_badge),
31+
resource_address: resource_address.resource_address(),
32+
tax: tax,
33+
tax_vault: Vault::new(resource_address),
34+
}
35+
component.instantiate();
36+
component.globalize()
37+
38+
}
39+
40+
pub fn take_tax() {
41+
/// take in bucket of token
42+
/// assert token address matches token address from component
43+
/// take tax from bucket, put in xrd_fees_collected vault in xrdao component
44+
/// pass remainder back to user
45+
/// present admin badge to allow the portion sent back to user to be used in trade/swap/transfer
46+
}
47+
48+
pub fn collect_taxes() {
49+
/// require xrdao_access_badge to call this method only
50+
/// takes all collected taxes and puts them in xrd_fees_collected in xrdao component
51+
taxes = self.tax_vault.empty();
52+
taxes // returns bucket of all collected fees
53+
54+
}
55+
56+
57+
}
58+
}

0 commit comments

Comments
 (0)