1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
#![cfg_attr(not(feature = "std"), no_std)]
18

            
19
use core::marker::PhantomData;
20
use frame_support::traits::{
21
    fungible::{InspectHold, Mutate, MutateHold},
22
    tokens::{Precision, Preservation},
23
};
24
use frame_support::{Deserialize, Serialize};
25
use pallet_stream_payment::HoldReason;
26
use parity_scale_codec::MaxEncodedLen;
27
use primitives::{AccountId, Balance, BlockNumber};
28
use sp_runtime::traits::{Block, Header};
29
use tp_traits::{apply, derive_storage_traits};
30

            
31
tp_traits::alias!(
32
    pub trait RuntimeConfigs:
33
        frame_system::Config<AccountId = AccountId, Block: Block<Header: Header<Number = BlockNumber>>> +
34
        pallet_balances::Config<Balance = Balance, RuntimeHoldReason: From<HoldReason>> +
35
        pallet_timestamp::Config<Moment = u64>
36
);
37

            
38
pub type StreamId = u64;
39

            
40
#[apply(derive_storage_traits)]
41
#[derive(Copy, Serialize, Deserialize, MaxEncodedLen)]
42
pub enum AssetId {
43
256
    Native,
44
    // TODO: Support more assets like foreign assets
45
}
46

            
47
pub struct AssetsManager<Runtime>(PhantomData<Runtime>);
48
impl<Runtime: RuntimeConfigs> pallet_stream_payment::AssetsManager<AccountId, AssetId, Balance>
49
    for AssetsManager<Runtime>
50
{
51
44
    fn transfer_deposit(
52
44
        asset_id: &AssetId,
53
44
        from: &AccountId,
54
44
        to: &AccountId,
55
44
        amount: Balance,
56
44
    ) -> frame_support::pallet_prelude::DispatchResult {
57
44
        match asset_id {
58
44
            AssetId::Native => {
59
44
                // We remove the hold before transfering.
60
44
                Self::decrease_deposit(asset_id, from, amount)?;
61
44
                pallet_balances::Pallet::<Runtime>::transfer(
62
44
                    from,
63
44
                    to,
64
44
                    amount,
65
44
                    Preservation::Preserve,
66
44
                )
67
44
                .map(|_| ())
68
            }
69
        }
70
44
    }
71

            
72
32
    fn increase_deposit(
73
32
        asset_id: &AssetId,
74
32
        account: &AccountId,
75
32
        amount: Balance,
76
32
    ) -> frame_support::pallet_prelude::DispatchResult {
77
32
        match asset_id {
78
32
            AssetId::Native => pallet_balances::Pallet::<Runtime>::hold(
79
32
                &HoldReason::StreamPayment.into(),
80
32
                account,
81
32
                amount,
82
32
            ),
83
32
        }
84
32
    }
85

            
86
60
    fn decrease_deposit(
87
60
        asset_id: &AssetId,
88
60
        account: &AccountId,
89
60
        amount: Balance,
90
60
    ) -> frame_support::pallet_prelude::DispatchResult {
91
60
        match asset_id {
92
60
            AssetId::Native => pallet_balances::Pallet::<Runtime>::release(
93
60
                &HoldReason::StreamPayment.into(),
94
60
                account,
95
60
                amount,
96
60
                Precision::Exact,
97
60
            )
98
60
            .map(|_| ()),
99
60
        }
100
60
    }
101

            
102
    fn get_deposit(asset_id: &AssetId, account: &AccountId) -> Balance {
103
        match asset_id {
104
            AssetId::Native => pallet_balances::Pallet::<Runtime>::balance_on_hold(
105
                &HoldReason::StreamPayment.into(),
106
                account,
107
            ),
108
        }
109
    }
110

            
111
    /// Benchmarks: should return the asset id which has the worst performance when interacting
112
    /// with it.
113
    #[cfg(feature = "runtime-benchmarks")]
114
    fn bench_worst_case_asset_id() -> AssetId {
115
        AssetId::Native
116
    }
117

            
118
    /// Benchmarks: should return the another asset id which has the worst performance when interacting
119
    /// with it afther `bench_worst_case_asset_id`. This is to benchmark the worst case when changing config
120
    /// from one asset to another.
121
    #[cfg(feature = "runtime-benchmarks")]
122
    fn bench_worst_case_asset_id2() -> AssetId {
123
        AssetId::Native
124
    }
125

            
126
    /// Benchmarks: should set the balance for the asset id returned by `bench_worst_case_asset_id`.
127
    #[cfg(feature = "runtime-benchmarks")]
128
    fn bench_set_balance(asset_id: &AssetId, account: &AccountId, amount: Balance) {
129
        use frame_support::traits::fungible::Mutate;
130

            
131
        // only one asset id
132
        let AssetId::Native = asset_id;
133

            
134
        pallet_balances::Pallet::<Runtime>::set_balance(account, amount);
135
    }
136
}
137

            
138
#[apply(derive_storage_traits)]
139
#[derive(Copy, Serialize, Deserialize, MaxEncodedLen)]
140
pub enum TimeUnit {
141
248
    BlockNumber,
142
8
    Timestamp,
143
    // TODO: Container chains/relay block number?
144
}
145

            
146
pub struct TimeProvider<Runtime>(PhantomData<Runtime>);
147
impl<Runtime: RuntimeConfigs> pallet_stream_payment::TimeProvider<TimeUnit, Balance>
148
    for TimeProvider<Runtime>
149
{
150
122
    fn now(unit: &TimeUnit) -> Option<Balance> {
151
122
        match *unit {
152
122
            TimeUnit::BlockNumber => Some(frame_system::Pallet::<Runtime>::block_number().into()),
153
            TimeUnit::Timestamp => Some(pallet_timestamp::Pallet::<Runtime>::get().into()),
154
        }
155
122
    }
156

            
157
    /// Benchmarks: should return the time unit which has the worst performance calling
158
    /// `TimeProvider::now(unit)` with.
159
    #[cfg(feature = "runtime-benchmarks")]
160
    fn bench_worst_case_time_unit() -> TimeUnit {
161
        // Both BlockNumber and Timestamp cost the same (1 db read), but overriding timestamp
162
        // doesn't work well in benches, while block number works fine.
163
        TimeUnit::BlockNumber
164
    }
165

            
166
    /// Benchmarks: sets the "now" time for time unit returned by `worst_case_time_unit`.
167
    #[cfg(feature = "runtime-benchmarks")]
168
    fn bench_set_now(instant: Balance) {
169
        frame_system::Pallet::<Runtime>::set_block_number(instant as u32)
170
    }
171
}