Skip to content

Commit

Permalink
Fees take token recipients into account
Browse files Browse the repository at this point in the history
  • Loading branch information
bchamagne committed Aug 16, 2023
1 parent 2e0c00d commit 1f5c683
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 6 deletions.
60 changes: 54 additions & 6 deletions lib/archethic/mining/fee.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ defmodule Archethic.Mining.Fee do
alias Archethic.TransactionChain.TransactionData.UCOLedger

@unit_uco 100_000_000
@token_creation_schema :archethic
|> Application.app_dir("priv/json-schemas/token-core.json")
|> File.read!()
|> Jason.decode!()
|> ExJsonSchema.Schema.resolve()

@token_resupply_schema :archethic
|> Application.app_dir("priv/json-schemas/token-resupply.json")
|> File.read!()
|> Jason.decode!()
|> ExJsonSchema.Schema.resolve()

@doc """
Determine the fee to paid for the given transaction
Expand Down Expand Up @@ -97,17 +108,22 @@ defmodule Archethic.Mining.Fee do
|> byte_size()
end

defp get_number_recipients(%Transaction{
data: %TransactionData{
ledger: %Ledger{
uco: %UCOLedger{transfers: uco_transfers},
token: %TokenLedger{transfers: token_transfers}
defp get_number_recipients(
tx = %Transaction{
data: %TransactionData{
ledger: %Ledger{
uco: %UCOLedger{transfers: uco_transfers},
token: %TokenLedger{transfers: token_transfers}
}
}
}
}) do
) do
(uco_transfers ++ token_transfers)
|> Enum.uniq_by(& &1.to)
|> length()
|> then(fn number ->
number + get_number_token_recipients(tx)
end)
end

defp get_number_replicas(%Transaction{address: address}, timestamp) do
Expand All @@ -134,4 +150,36 @@ defmodule Archethic.Mining.Fee do
defp cost_per_recipients(nb_recipients, uco_price_in_usd) do
nb_recipients * (0.1 / uco_price_in_usd)
end

defp get_number_token_recipients(%Transaction{
type: :token,
data: %TransactionData{content: content}
}) do
case Jason.decode(content) do
{:ok, json} ->
cond do
ExJsonSchema.Validator.valid?(@token_creation_schema, json) ->
count_token_recipients_from_json(json)

ExJsonSchema.Validator.valid?(@token_resupply_schema, json) ->
count_token_recipients_from_json(json)

true ->
0
end

{:error, _} ->
0
end
end

defp get_number_token_recipients(_tx), do: 0

defp count_token_recipients_from_json(%{"recipients" => recipients}) do
recipients
|> Enum.uniq_by(& &1["to"])
|> length()
end

defp count_token_recipients_from_json(_json), do: 0
end
113 changes: 113 additions & 0 deletions test/archethic/mining/fee_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule Archethic.Mining.FeeTest do
use ArchethicCase

import ArchethicCase

alias Archethic.Mining.Fee

alias Archethic.P2P
Expand Down Expand Up @@ -91,6 +93,117 @@ defmodule Archethic.Mining.FeeTest do
|> Fee.calculate(2.0, DateTime.utc_now())
end

test "should take token unique recipients into account (token creation)" do
address1 = random_address()
# 0.21 UCO for 4 recipients (3 unique in content + 1 in ledger) + 1 token at $2.0
assert 21_016_950 ==
%Transaction{
address: <<0::8, :crypto.strong_rand_bytes(32)::binary>>,
type: :token,
data: %TransactionData{
content: """
{
"aeip": [2, 8, 19],
"supply": 300000000,
"type": "fungible",
"name": "My token",
"symbol": "MTK",
"properties": {},
"recipients": [
{
"to": "#{Base.encode16(address1)}",
"amount": 100000000
},
{
"to": "#{Base.encode16(address1)}",
"amount": 100000000
},
{
"to": "#{Base.encode16(random_address())}",
"amount": 100000000
},
{
"to": "#{Base.encode16(random_address())}",
"amount": 100000000
}
]
}
""",
ledger: %Ledger{
uco: %UCOLedger{
transfers: [
%Transfer{
amount: 100_000_000,
to: <<0::8, :crypto.strong_rand_bytes(32)::binary>>
}
]
}
}
},
previous_public_key: <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>,
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(2.0, DateTime.utc_now())
end

test "should take token unique recipients into account (token resupply)" do
# 0.11 UCO for 2 recipients + 1 token at $2.0
assert 11_010_100 ==
%Transaction{
address: <<0::8, :crypto.strong_rand_bytes(32)::binary>>,
type: :token,
data: %TransactionData{
content: """
{
"aeip": [8, 18],
"supply": 1000,
"token_reference": "0000C13373C96538B468CCDAB8F95FDC3744EBFA2CD36A81C3791B2A205705D9C3A2",
"recipients": [
{
"to": "#{Base.encode16(random_address())}",
"amount": 100000000
},
{
"to": "#{Base.encode16(random_address())}",
"amount": 100000000
}
]
}
"""
},
previous_public_key: <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>,
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(2.0, DateTime.utc_now())
end

test "should pay additional fee for tokens without recipient" do
# 0.01 UCO for 0 transfer + 1 token at $2.0
assert 1_003_524 ==
%Transaction{
address: <<0::8, :crypto.strong_rand_bytes(32)::binary>>,
type: :token,
data: %TransactionData{
content: """
{
"aeip": [2, 8, 19],
"supply": 300000000,
"type": "fungible",
"name": "My token",
"symbol": "MTK",
"properties": {}
}
"""
},
previous_public_key: <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>,
previous_signature: :crypto.strong_rand_bytes(32),
origin_signature: :crypto.strong_rand_bytes(32)
}
|> Fee.calculate(2.0, DateTime.utc_now())
end

test "should decrease the fee when the amount stays the same but the price of UCO increases" do
# 0.00501425 UCO for 1 UCO at $ 2.0
assert 501_425 =
Expand Down

0 comments on commit 1f5c683

Please sign in to comment.