-
Notifications
You must be signed in to change notification settings - Fork 2
/
IOnchain.sol
120 lines (101 loc) · 4.43 KB
/
IOnchain.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
pragma ton-solidity >= 0.39.0;
import "./Fallbacks.sol";
import "./IOnchainCallbacks.sol";
import "../Service.sol";
import "../../node_modules/@broxus/contracts/contracts/libraries/MsgFlag.sol";
struct UserData {
address user;
TvmCell actionPayload;
}
abstract contract IOnchain {
address _root;
address _service;
address[] _subscriptionPlans;
uint128 _minValue;
mapping(address /*userSubscription*/ => UserData) _waitingUsers;
/*
@param root root address
@param service service address
@param subscriptionPlans supported subscription plans of service
@param minValue min value for checking subscription, recommend to use 1 ton
*/
constructor(address root, address service, address[] subscriptionPlans, uint128 minValue) public {
tvm.accept();
_root = root;
_service = service;
_subscriptionPlans = subscriptionPlans;
_minValue = minValue;
}
function isSupportedSubscriptionPlan(address subscriptionPlans) public view returns (bool) {
for (uint32 i = 0; i < _subscriptionPlans.length; i++)
if (_subscriptionPlans[i] == subscriptionPlans)
return true;
return false;
}
/*
Check if `msg.sender` in subscribed, then call `_action`
@param subscriptionPlan address of subscription of `msg.sender`
@param actionPayload payload that will be sent to `_action`
@value must more or equal to `_minValue`, otherwise fallback will be called
*/
function _checkSubscription(address subscriptionPlan, TvmCell actionPayload) internal view {
if (msg.value < _minValue) {
IOnchainCallbacks(msg.sender).onchainFallback{value: 0, flag: MsgFlag.REMAINING_GAS}(Fallbacks.NOT_ENOUGH_TOKENS);
return;
}
if (!isSupportedSubscriptionPlan(subscriptionPlan)) {
IOnchainCallbacks(msg.sender).onchainFallback{value: 0, flag: MsgFlag.REMAINING_GAS}(Fallbacks.UNSUPPORTED_SUBSCRIPTION_PLAN);
return;
}
address user = msg.sender;
TvmBuilder builder;
builder.store(UserData(user, actionPayload));
TvmCell payload = builder.toCell();
SubscriptionPlan(subscriptionPlan)
.getUserSubscriptionWithPayload{
value: 0,
flag: MsgFlag.REMAINING_GAS,
callback: userSubscriptionCallback,
bounce: false // cannot be bounced
}(user, 0, payload);
}
function userSubscriptionCallback(address userSubscription, TvmCell payload) public {
require(isSupportedSubscriptionPlan(msg.sender), 999, 'Hack attempt');
UserData userData = payload.toSlice().decode(UserData);
_waitingUsers[userSubscription] = userData;
UserSubscription(userSubscription)
.isActive{
value: 0,
flag: MsgFlag.REMAINING_GAS,
callback: isActiveCallback,
bounce: true // can be bounced
}();
}
function isActiveCallback(bool active) public {
require(_waitingUsers.exists(msg.sender), 999, 'Hack attempt');
UserData userData = _waitingUsers[msg.sender];
delete _waitingUsers[msg.sender];
if (active) {
IOnchainCallbacks(userData.user).onchainSuccess{value: 0, flag: MsgFlag.REMAINING_GAS}();
_action(userData.user, userData.actionPayload);
} else {
IOnchainCallbacks(userData.user).onchainFallback{value: 0, flag: MsgFlag.REMAINING_GAS}(Fallbacks.SUBSCRIPTION_IS_EXPIRED);
}
}
/*
This method is called after success checking, override it
@param user `msg.sender` address and user who have a subscription
@param payload initial payloaf
@param subscriptionPlans supported subscription plans of service
@param minValue min value for checking subscription, recommend to use 1 ton
*/
function _action(address user, TvmCell payload) internal virtual;
onBounce(TvmSlice slice) external {
uint32 functionId = slice.decode(uint32);
if (functionId == tvm.functionId(UserSubscription.isActive)) {
UserData userData = _waitingUsers[msg.sender];
delete _waitingUsers[msg.sender];
IOnchainCallbacks(userData.user).onchainFallback{value: 0, flag: MsgFlag.REMAINING_GAS}(Fallbacks.SUBSCRIPTION_IS_NOT_EXISTS);
}
}
}