Skip to content

Commit b8cbcdd

Browse files
EzePzemicahkendall
andcommitted
Contracts Source-Available
Co-authored-by: Micah Kendall <micahk.accounts@icloud.com>
1 parent 536fd29 commit b8cbcdd

23 files changed

+4647
-0
lines changed

.github/workflows/ci.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
# cancel in-progress runs on new commits to same PR (gitub.event.number)
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
build:
16+
runs-on: ubuntu-latest
17+
continue-on-error: true
18+
steps:
19+
- name: 📥 Checkout repository
20+
uses: actions/checkout@v4
21+
22+
- name: Setup Aiken
23+
uses: aiken-lang/setup-aiken@v1
24+
with:
25+
version: v1.0.29-alpha
26+
27+
- name: 🧪 Run fmt check
28+
run: aiken fmt --check
29+
30+
- name: 🧪 Run lint
31+
run: aiken check -s -D
32+
33+
- name: 🎁 Run build
34+
run: aiken build
35+
36+
- name: 🧪 Run test
37+
run: aiken check

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# butane-contracts
2+
3+
Smart contracts for the Butane Protocol.

aiken.lock

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# This file was generated by Aiken
2+
# You typically do not need to edit this file
3+
4+
[[requirements]]
5+
name = "aiken-lang/stdlib"
6+
version = "1.9.0"
7+
source = "github"
8+
9+
[[requirements]]
10+
name = "aiken-lang/fuzz"
11+
version = "1.0.0"
12+
source = "github"
13+
14+
[[packages]]
15+
name = "aiken-lang/stdlib"
16+
version = "1.9.0"
17+
requirements = []
18+
source = "github"
19+
20+
[[packages]]
21+
name = "aiken-lang/fuzz"
22+
version = "1.0.0"
23+
requirements = []
24+
source = "github"
25+
26+
[etags]

aiken.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name = "butaneprotocol/butane-contracts"
2+
version = "0.0.1"
3+
licenses = []
4+
description = "Contracts for the Butane Protocol"
5+
dependencies = [
6+
{ name = "aiken-lang/stdlib", version = "1.9.0", source = "github" },
7+
{ name = "aiken-lang/fuzz", version = "1.0.0", source = "github" },
8+
]

lib/butane/prices.ak

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
use aiken/builtin
2+
use aiken/dict.{Dict}
3+
use aiken/math
4+
use aiken/math/rational.{Rational}
5+
use aiken/transaction/value.{AssetName, PolicyId, Value}
6+
use butane/types
7+
use butane/unsafe
8+
9+
// Gets the raw collateralization ratio (CR) and health factor (HF) of a CDP
10+
pub fn get_collateral_finances(
11+
p_list: List<Int>,
12+
p_dom: Int,
13+
v: Value,
14+
assets: List<types.AssetClass>,
15+
w_list: List<Int>,
16+
w_dom: Int,
17+
max_proportions_list: List<Int>,
18+
synth_amount: Int,
19+
callback: fn(Rational, Rational) -> a,
20+
) -> a {
21+
let v2: Pairs<PolicyId, Dict<AssetName, Int>> =
22+
v |> value.to_dict |> dict.to_pairs
23+
let unweighted_capacity, capacity <-
24+
do_get_borrowing_capacity(
25+
u_value: v2,
26+
debt: synth_amount,
27+
unweighted_capacity: 0,
28+
borrowing_capacity: 0,
29+
collateral_assets: assets,
30+
collateral_prices: p_list,
31+
prices_denom: p_dom,
32+
weights_denom: w_dom,
33+
collateral_weights: w_list,
34+
collateral_max_proportions: max_proportions_list,
35+
callback: _,
36+
)
37+
let cr =
38+
unsafe.unsome(rational.new(unweighted_capacity, p_dom * synth_amount))
39+
let hf = unsafe.unsome(rational.new(capacity, synth_amount))
40+
callback(cr, hf)
41+
}
42+
43+
pub fn do_get_borrowing_capacity(
44+
u_value: Pairs<PolicyId, Dict<AssetName, Int>>,
45+
debt: Int,
46+
unweighted_capacity: Int,
47+
borrowing_capacity: Int,
48+
collateral_assets: List<types.AssetClass>,
49+
collateral_prices: List<Int>,
50+
prices_denom: Int,
51+
weights_denom: Int,
52+
collateral_weights: List<Int>,
53+
collateral_max_proportions: List<Int>,
54+
callback: fn(Int, Int) -> a,
55+
) -> a {
56+
when u_value is {
57+
[Pair(pol, pol_v), ..vs] ->
58+
do_get_borrowing_capacity_2(
59+
pol: pol,
60+
pol_v: pol_v |> dict.to_pairs,
61+
and_finally: do_get_borrowing_capacity(vs, _, _, _, _, _, _, _, _, _, _),
62+
debt: debt,
63+
unweighted_capacity: unweighted_capacity,
64+
borrowing_capacity: borrowing_capacity,
65+
collateral_assets: collateral_assets,
66+
collateral_prices: collateral_prices,
67+
prices_denom: prices_denom,
68+
weights_denom: weights_denom,
69+
collateral_weights: collateral_weights,
70+
collateral_max_proportions: collateral_max_proportions,
71+
callback: callback,
72+
)
73+
_ -> callback(unweighted_capacity, borrowing_capacity)
74+
}
75+
}
76+
77+
pub fn do_get_borrowing_capacity_2(
78+
pol: PolicyId,
79+
pol_v: Pairs<AssetName, Int>,
80+
and_finally: fn(
81+
Int,
82+
Int,
83+
Int,
84+
List<types.AssetClass>,
85+
List<Int>,
86+
Int,
87+
Int,
88+
List<Int>,
89+
List<Int>,
90+
fn(Int, Int) -> a,
91+
) ->
92+
a,
93+
debt: Int,
94+
unweighted_capacity: Int,
95+
borrowing_capacity: Int,
96+
collateral_assets: List<types.AssetClass>,
97+
collateral_prices: List<Int>,
98+
prices_denom: Int,
99+
weights_denom: Int,
100+
collateral_weights: List<Int>,
101+
collateral_max_proportions: List<Int>,
102+
callback: fn(Int, Int) -> a,
103+
) -> a {
104+
when pol_v is {
105+
[Pair(token_name, qty), ..vs] ->
106+
get_asset_price_then(
107+
s: types.AssetClass { policy_id: pol, asset_name: token_name },
108+
debt: debt,
109+
unweighted_capacity: unweighted_capacity,
110+
borrowing_capacity: borrowing_capacity,
111+
qty: qty,
112+
asset_list: collateral_assets,
113+
price_list: collateral_prices,
114+
prices_denom: prices_denom,
115+
weights_denom: weights_denom,
116+
weights_list: collateral_weights,
117+
max_proportion_list: collateral_max_proportions,
118+
continue_with: do_get_borrowing_capacity_2(
119+
pol,
120+
vs,
121+
and_finally,
122+
_,
123+
_,
124+
_,
125+
_,
126+
_,
127+
_,
128+
_,
129+
_,
130+
_,
131+
_,
132+
),
133+
callback: callback,
134+
)
135+
_ ->
136+
and_finally(
137+
debt,
138+
unweighted_capacity,
139+
borrowing_capacity,
140+
collateral_assets,
141+
collateral_prices,
142+
prices_denom,
143+
weights_denom,
144+
collateral_weights,
145+
collateral_max_proportions,
146+
callback,
147+
)
148+
}
149+
}
150+
151+
fn get_asset_price_then(
152+
s: types.AssetClass,
153+
debt: Int,
154+
unweighted_capacity: Int,
155+
borrowing_capacity: Int,
156+
qty: Int,
157+
asset_list: List<types.AssetClass>,
158+
price_list: List<Int>,
159+
prices_denom: Int,
160+
weights_denom: Int,
161+
weights_list: List<Int>,
162+
max_proportion_list: List<Int>,
163+
continue_with: fn(
164+
Int,
165+
Int,
166+
Int,
167+
List<types.AssetClass>,
168+
List<Int>,
169+
Int,
170+
Int,
171+
List<Int>,
172+
List<Int>,
173+
fn(Int, Int) -> a,
174+
) ->
175+
a,
176+
callback: fn(Int, Int) -> a,
177+
) -> a {
178+
expect [asset_name, ..] = asset_list
179+
if s == asset_name {
180+
expect [price, ..] = price_list
181+
let numerator = qty * price
182+
continue_with(
183+
debt,
184+
unweighted_capacity + numerator,
185+
borrowing_capacity + math.min(
186+
// First case: the asset is not limited by the max proportion
187+
numerator * weights_denom / builtin.head_list(weights_list) / prices_denom,
188+
// Second case: the asset is limited by the max proportion
189+
debt * builtin.head_list(max_proportion_list) / types.bp_precision,
190+
),
191+
builtin.tail_list(asset_list),
192+
builtin.tail_list(price_list),
193+
prices_denom,
194+
weights_denom,
195+
builtin.tail_list(weights_list),
196+
builtin.tail_list(max_proportion_list),
197+
callback,
198+
)
199+
} else {
200+
get_asset_price_then(
201+
s: s,
202+
debt: debt,
203+
unweighted_capacity: unweighted_capacity,
204+
borrowing_capacity: borrowing_capacity,
205+
qty: qty,
206+
asset_list: builtin.tail_list(asset_list),
207+
price_list: builtin.tail_list(price_list),
208+
prices_denom: prices_denom,
209+
weights_denom: weights_denom,
210+
weights_list: builtin.tail_list(weights_list),
211+
max_proportion_list: builtin.tail_list(max_proportion_list),
212+
continue_with: continue_with,
213+
callback: callback,
214+
)
215+
}
216+
}
217+
218+
test basic_bc_test() {
219+
let
220+
a,
221+
b,
222+
<-
223+
do_get_borrowing_capacity(
224+
value.from_asset("", "", 10)
225+
|> value.to_dict
226+
|> dict.to_pairs,
227+
100,
228+
0,
229+
0,
230+
[types.AssetClass { policy_id: "", asset_name: "" }],
231+
[2],
232+
1,
233+
1,
234+
[3],
235+
[10_000],
236+
)
237+
(a, b) == (20, 6)
238+
}
239+
240+
test basic_bc_test_2() {
241+
let
242+
a,
243+
b,
244+
<-
245+
do_get_borrowing_capacity(
246+
value.from_asset("", "", 10)
247+
|> value.merge(value.from_asset("a", "b", 20))
248+
|> value.to_dict
249+
|> dict.to_pairs,
250+
100,
251+
0,
252+
0,
253+
[
254+
types.AssetClass { policy_id: "", asset_name: "" },
255+
types.AssetClass { policy_id: "a", asset_name: "b" },
256+
],
257+
[2, 2],
258+
1,
259+
1,
260+
[3, 3],
261+
[10_000, 10_000],
262+
)
263+
(a, b) == (60, 19)
264+
}
265+
266+
test basic_bc_test_m() {
267+
let
268+
a,
269+
b,
270+
<-
271+
do_get_borrowing_capacity(
272+
value.from_asset("", "", 10)
273+
|> value.merge(value.from_asset("a", "b", 20))
274+
|> value.merge(value.from_asset("c", "d", 20))
275+
|> value.merge(value.from_asset("e", "f", 20))
276+
|> value.merge(value.from_asset("g", "h", 20))
277+
|> value.merge(value.from_asset("i", "j", 20))
278+
|> value.to_dict
279+
|> dict.to_pairs,
280+
100,
281+
0,
282+
0,
283+
[
284+
types.AssetClass { policy_id: "", asset_name: "" },
285+
types.AssetClass { policy_id: "a", asset_name: "b" },
286+
types.AssetClass { policy_id: "c", asset_name: "d" },
287+
types.AssetClass { policy_id: "e", asset_name: "f" },
288+
types.AssetClass { policy_id: "g", asset_name: "h" },
289+
types.AssetClass { policy_id: "i", asset_name: "j" },
290+
],
291+
[2, 2, 2, 2, 2, 2],
292+
1,
293+
1,
294+
[3, 3, 3, 3, 3, 3],
295+
[10_000, 10_000, 10_000, 10_000, 10_000, 10_000],
296+
)
297+
(a, b) == (220, 71)
298+
}

0 commit comments

Comments
 (0)